Promise的使用

在JavaScript的世界中荣茫,所有代碼都是單線程執(zhí)行的。

由于這個“缺陷”睁本,導致JavaScript的所有網(wǎng)絡操作尿庐,瀏覽器事件,都必須是異步執(zhí)行呢堰。異步執(zhí)行可以用回調函數(shù)實現(xiàn):

functioncallback(){console.log('Done');}console.log('before setTimeout()');setTimeout(callback,1000);// 1秒鐘后調用callback函數(shù)console.log('after setTimeout()');

觀察上述代碼執(zhí)行抄瑟,在Chrome的控制臺輸出可以看到:

before setTimeout()

after setTimeout()

(等待1秒后)

Done

可見,異步操作會在將來的某個時間點觸發(fā)一個函數(shù)調用枉疼。

AJAX就是典型的異步操作皮假。以上一節(jié)的代碼為例:

request.onreadystatechange =function(){if(request.readyState ===4) {if(request.status ===200) {returnsuccess(request.responseText);? ? ? ? }else{returnfail(request.status);? ? ? ? }? ? }}

把回調函數(shù)success(request.responseText)和fail(request.status)寫到一個AJAX操作里很正常,但是不好看骂维,而且不利于代碼復用惹资。

有沒有更好的寫法?比如寫成這樣:

varajax = ajaxGet('http://...');ajax.ifSuccess(success)? ? .ifFail(fail);

這種鏈式寫法的好處在于航闺,先統(tǒng)一執(zhí)行AJAX邏輯褪测,不關心如何處理結果,然后潦刃,根據(jù)結果是成功還是失敗汰扭,在將來的某個時候調用success函數(shù)或fail函數(shù)。

古人云:“君子一諾千金”福铅,這種“承諾將來會執(zhí)行”的對象在JavaScript中稱為Promise對象萝毛。

Promise有各種開源實現(xiàn),在ES6中被統(tǒng)一規(guī)范滑黔,由瀏覽器直接支持笆包。先測試一下你的瀏覽器是否支持Promise:

'use strict';

new Promise(function () {});

?Run

支持Promise!

我們先看一個最簡單的Promise例子:生成一個0-2之間的隨機數(shù),如果小于1略荡,則等待一段時間后返回成功庵佣,否則返回失敗:

functiontest(resolve, reject){vartimeOut = Math.random() *2;? ? log('set timeout to: '+ timeOut +' seconds.');? ? setTimeout(function(){if(timeOut <1) {? ? ? ? ? ? log('call resolve()...');? ? ? ? ? ? resolve('200 OK');? ? ? ? }else{? ? ? ? ? ? log('call reject()...');? ? ? ? ? ? reject('timeout in '+ timeOut +' seconds.');? ? ? ? }? ? }, timeOut *1000);}

這個test()函數(shù)有兩個參數(shù)汛兜,這兩個參數(shù)都是函數(shù)巴粪,如果執(zhí)行成功,我們將調用resolve('200 OK')粥谬,如果執(zhí)行失敗肛根,我們將調用reject('timeout in ' + timeOut + ' seconds.')÷┎撸可以看出派哲,test()函數(shù)只關心自身的邏輯,并不關心具體的resolve和reject將如何處理結果掺喻。

有了執(zhí)行函數(shù)芭届,我們就可以用一個Promise對象來執(zhí)行它储矩,并在將來某個時刻獲得成功或失敗的結果:

varp1 =newPromise(test);varp2 = p1.then(function(result){console.log('成功:'+ result);});varp3 = p2.catch(function(reason){console.log('失敗:'+ reason);});

變量p1是一個Promise對象褂乍,它負責執(zhí)行test函數(shù)持隧。由于test函數(shù)在內部是異步執(zhí)行的,當test函數(shù)執(zhí)行成功時逃片,我們告訴Promise對象:

// 如果成功屡拨,執(zhí)行這個函數(shù):p1.then(function(result){console.log('成功:'+ result);});

當test函數(shù)執(zhí)行失敗時,我們告訴Promise對象:

p2.catch(function(reason){console.log('失斕馑小:'+ reason);});

Promise對象可以串聯(lián)起來洁仗,所以上述代碼可以簡化為:

newPromise(test).then(function(result){console.log('成功:'+ result);}).catch(function(reason){console.log('失敗:'+ reason);});

實際測試一下性锭,看看Promise是如何異步執(zhí)行的:

'use strict';

// 清除log:

var logging = document.getElementById('test-promise-log');

while (logging.children.length > 1) {

? ? logging.removeChild(logging.children[logging.children.length - 1]);

}

// 輸出log到頁面:

function log(s) {

? ? var p = document.createElement('p');

? ? p.innerHTML = s;

? ? logging.appendChild(p);

}

?Run

(no?output)

Log:

start new Promise...

set timeout to: 0.08028750076878355 seconds.

call resolve()...

Done: 200 OK

可見Promise最大的好處是在異步執(zhí)行的流程中赠潦,把執(zhí)行代碼和處理結果的代碼清晰地分離了:

Promise還可以做更多的事情,比如草冈,有若干個異步任務她奥,需要先做任務1,如果成功后再做任務2怎棱,任何任務失敗則不再繼續(xù)并執(zhí)行錯誤處理函數(shù)哩俭。

要串行執(zhí)行這樣的異步任務,不用Promise需要寫一層一層的嵌套代碼拳恋。有了Promise凡资,我們只需要簡單地寫:

job1.then(job2).then(job3).catch(handleError);

其中,job1谬运、job2和job3都是Promise對象隙赁。

下面的例子演示了如何串行執(zhí)行一系列需要異步計算獲得結果的任務:

'use strict';

var logging = document.getElementById('test-promise2-log');

while (logging.children.length > 1) {

? ? logging.removeChild(logging.children[logging.children.length - 1]);

}

function log(s) {

? ? var p = document.createElement('p');

? ? p.innerHTML = s;

? ? logging.appendChild(p);

}

?Run

(no?output)

Log:

start new Promise...

calculating 123 x 123...

calculating 15129 + 15129...

calculating 30258 x 30258...

calculating 915546564 + 915546564...

Got value: 1831093128

setTimeout可以看成一個模擬網(wǎng)絡等異步執(zhí)行的函數(shù)。現(xiàn)在梆暖,我們把上一節(jié)的AJAX異步執(zhí)行函數(shù)轉換為Promise對象伞访,看看用Promise如何簡化異步處理:

'use strict';

// ajax函數(shù)將返回Promise對象:

function ajax(method, url, data) {

? ? var request = new XMLHttpRequest();

? ? return new Promise(function (resolve, reject) {

? ? ? ? request.onreadystatechange = function () {

? ? ? ? ? ? if (request.readyState === 4) {

? ? ? ? ? ? ? ? if (request.status === 200) {

? ? ? ? ? ? ? ? ? ? resolve(request.responseText);

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? reject(request.status);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? request.open(method, url);

? ? ? ? request.send(data);

? ? });

}

?Run

(no?output)

{"categories":[{"id":"0013738748415562fee26e070fa4664ad926c8e30146c67000","name":"編程","tag":"tech","display_order":0,"description":"","created_at":1373874841556,"updated_at":1429763779958,"version":5},{"id":"0013738748248885ddf38d8cd1b4803aa74bcda32f853fd000","name":"讀書","tag":"other","display_order":1,"description":"","created_at":1373874824888,"updated_at":1429763779974,"version":5}]}

除了串行執(zhí)行若干異步任務外,Promise還可以并行執(zhí)行異步任務轰驳。

試想一個頁面聊天系統(tǒng)厚掷,我們需要從兩個不同的URL分別獲得用戶的個人信息和好友列表,這兩個任務是可以并行執(zhí)行的级解,用Promise.all()實現(xiàn)如下:

varp1 =newPromise(function(resolve, reject){setTimeout(resolve,500,'P1');});varp2 =newPromise(function(resolve, reject){setTimeout(resolve,600,'P2');});// 同時執(zhí)行p1和p2冒黑,并在它們都完成后執(zhí)行then:Promise.all([p1, p2]).then(function(results){console.log(results);// 獲得一個Array: ['P1', 'P2']});

有些時候,多個異步任務是為了容錯蠕趁。比如薛闪,同時向兩個URL讀取用戶的個人信息,只需要獲得先返回的結果即可俺陋。這種情況下豁延,用Promise.race()實現(xiàn):

varp1 =newPromise(function(resolve, reject){setTimeout(resolve,500,'P1');});varp2 =newPromise(function(resolve, reject){setTimeout(resolve,600,'P2');});Promise.race([p1, p2]).then(function(result){console.log(result);// 'P1'});

由于p1執(zhí)行較快,Promise的then()將獲得結果'P1'腊状。p2仍在繼續(xù)執(zhí)行诱咏,但執(zhí)行結果將被丟棄。

如果我們組合使用Promise缴挖,就可以把很多異步任務以并行和串行的方式組合起來執(zhí)行袋狞。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市映屋,隨后出現(xiàn)的幾起案子苟鸯,更是在濱河造成了極大的恐慌,老刑警劉巖棚点,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件早处,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘫析,警方通過查閱死者的電腦和手機砌梆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贬循,“玉大人咸包,你說我怎么就攤上這事≌认海” “怎么了烂瘫?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奇适。 經(jīng)常有香客問我坟比,道長,這世上最難降的妖魔是什么滤愕? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任温算,我火速辦了婚禮,結果婚禮上间影,老公的妹妹穿的比我還像新娘注竿。我一直安慰自己,他們只是感情好魂贬,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布巩割。 她就那樣靜靜地躺著,像睡著了一般付燥。 火紅的嫁衣襯著肌膚如雪宣谈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天键科,我揣著相機與錄音闻丑,去河邊找鬼漩怎。 笑死,一個胖子當著我的面吹牛嗦嗡,可吹牛的內容都是我干的勋锤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼侥祭,長吁一口氣:“原來是場噩夢啊……” “哼叁执!你這毒婦竟也來了?” 一聲冷哼從身側響起矮冬,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谈宛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胎署,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吆录,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年硝拧,在試婚紗的時候發(fā)現(xiàn)自己被綠了径筏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡障陶,死狀恐怖滋恬,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情抱究,我是刑警寧澤恢氯,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鼓寺,受9級特大地震影響勋拟,放射性物質發(fā)生泄漏。R本人自食惡果不足惜妈候,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一敢靡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苦银,春花似錦啸胧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陷谱。三九已至瑟蜈,卻和暖如春渣窜,著一層夾襖步出監(jiān)牢的瞬間焙格,已是汗流浹背夷都。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工囤官, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肝陪。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓刑顺,卻偏偏與公主長得像蹲堂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子柒竞,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內容

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案朽基,比傳統(tǒng)的解決方案——回調函...
    neromous閱讀 8,706評論 1 56
  • 一稼虎、Promise的含義 Promise在JavaScript語言中早有實現(xiàn),ES6將其寫進了語言標準霎俩,統(tǒng)一了用法...
    Alex灌湯貓閱讀 824評論 0 2
  • 什么叫promise? 那我們?yōu)槭裁匆褂胮romise茸苇? 比如我們在工作中經(jīng)常會碰到這么一個需求,比如我使用aj...
    紫陌蘭溪閱讀 14,643評論 2 5
  • 前言 本文旨在簡單講解一下javascript中的Promise對象的概念淘衙,特性與簡單的使用方法腻暮。并在文末會附上一...
    _暮雨清秋_閱讀 2,200評論 0 3
  • Promise 是異步編程的一種解決方案毯侦。簡單來說 Promise 就是一個容器具垫,里面保存著某個未來才會結束的異步...
    點融黑幫閱讀 786評論 0 13