JavaScript異步Promise

JavaScript里通常不建議阻塞主程序,尤其是一些代價(jià)比較昂貴的操作速种,如查找數(shù)據(jù)庫姜盈,下載文件等操作,應(yīng)該用異步API配阵,如Ajax模式馏颂。在執(zhí)行操作的的同時(shí)示血,主程序能繼續(xù)處理后序代碼,等操作結(jié)束后調(diào)用回調(diào)函數(shù)救拉。

例如文件下載矾芙,同步方式的話,萬一網(wǎng)絡(luò)情緒不穩(wěn)定近上,下載文件耗時(shí)很長剔宪,頁面就卡住了,所以一定會用異步API:

downloadAsync("http://example.com/file.txt", function(text) {
    console.log(text);
});

但通常異步操作并非只有單一步驟壹无,就以文件下載為例葱绒,如果下載3個(gè)有關(guān)聯(lián)的文件,你要如何才能保證依次下載呢斗锭?你可能會用回調(diào)嵌套:

downloadAsync("a.txt", function(a) {
    downloadAsync("b.txt", function(b) {
        downloadAsync("c.txt", function(c) {
            console.log("Contents: " + a + b + c);
        }, function(error) {
            console.log("Error: " + error);
        });
    }, function(error) {
        console.log("Error: " + error);
    });
}, function(error) {
    console.log("Error: " + error);
});

回調(diào)嵌套本身沒有什么錯(cuò)地淀。它被詬病之處在于,這才3層岖是,基本框架就很難看了帮毁。業(yè)務(wù)邏輯稍微多一點(diǎn),即便你努力重構(gòu)出子函數(shù)豺撑,也難以避免讓主體框架陷入“回調(diào)地獄”烈疚,代碼會難以閱讀和維護(hù)。

這就是Promise誕生的原因聪轿,它并不是全新的數(shù)據(jù)類型爷肝,而是一種組織代碼的方式,內(nèi)部改進(jìn)了回調(diào)函數(shù)陆错,對外暴露出then方法灯抛。讓開發(fā)者避免陷入“回調(diào)地獄”,寫出更加清晰的音瓷,類似函數(shù)鏈的代碼对嚼。ES6將Promise納入了標(biāo)準(zhǔn),本篇就介紹一下Promise绳慎。

  • Promise對象
  • then方法
  • catch方法
  • 靜態(tài)方法(resolve纵竖,reject,all偷线,race)
  • 例子

Promise對象

Promise既然是一種代碼組織方式磨确,它就需要為用戶提供構(gòu)造函數(shù)來創(chuàng)建一個(gè)Promise對象。構(gòu)造函數(shù)原型:new Promise(function(resolve, reject) { … } );声邦。參照MDN

構(gòu)造函數(shù)用一個(gè)函數(shù)作為參數(shù)乏奥,該函數(shù)有兩個(gè)參數(shù),兩個(gè)參數(shù)均是回調(diào)函數(shù)亥曹,由JS引擎提供邓了,你不用自己部署了恨诱。第一個(gè)參數(shù)resolve,當(dāng)異步操作成功時(shí)會調(diào)用骗炉,它有一個(gè)參數(shù)用于傳遞異步操作成功的結(jié)果照宝。第二個(gè)參數(shù)reject,當(dāng)異步操作失敗時(shí)會調(diào)用句葵,它有一個(gè)參數(shù)用于傳遞異步操作失敗的信息厕鹃。例如:

var myPromise = new Promise(function(resolve, reject) {
    ...  //異步操作
    if( success ) {
        resolve(value);
    } else {
        reject(error);
    }
});

上面生成了一個(gè)Promise對象,它代表著一個(gè)異步操作乍丈。有3種狀態(tài)Pending剂碴,Resolved(MDN里又稱Fulfilled),Rejected轻专∫涿看名字就知道分別表示異步操作中,操作成功请垛,操作失敗催训。

一旦調(diào)用構(gòu)造函數(shù)開始生成Promise對象(如上面myPromise),就會立即執(zhí)行異步操作宗收。異步操作成功的話漫拭,myPromise狀態(tài)會從Pending切換成Resolved。失敗的話镜雨,狀態(tài)從Pending切換成Rejected儿捧。狀態(tài)改變后就固定了颓影,永遠(yuǎn)不會再次改變临谱,這就是Promise的語義城豁,表示“承諾”。一旦承諾,盒突恚枯石爛都不會變驼壶。實(shí)例對象生成后,就能調(diào)用then方法了碟渺。

then方法

Promise.prototype.then()方法顯然就是Promise的精華绒极。函數(shù)聲明:p.then(resolve, reject);。參照MDN

注意它不是靜態(tài)方法审丘,需要經(jīng)由Promise實(shí)例對象來調(diào)用喉镰。

then方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是Promise實(shí)例對象為Resolved狀態(tài)時(shí)的回調(diào)函數(shù),它的參數(shù)就是上面Promise構(gòu)造函數(shù)里resolve傳遞過來的異步操作成功的結(jié)果昼牛。

第二個(gè)參數(shù)可選辜伟,是Promise實(shí)例對象為Rejected狀態(tài)時(shí)的回調(diào)函數(shù)尝蠕,它的參數(shù)就是上面Promise構(gòu)造函數(shù)里reject傳遞過來的異步操作失敗的信息。

例如:

var myPromise = new Promise(function(resolve, reject) {
    console.log("執(zhí)行異步操作");
    resolve("成功");
    //reject ("失敗啦");
});
console.log("后續(xù)操作");

myPromise.then(function(value) {
    console.log(value);
}, function(error) {
     console.log(error);
});
//執(zhí)行異步操作
//后續(xù)操作
//成功

上面結(jié)果可以看出星压,一旦調(diào)用構(gòu)造函數(shù)開始生成Promise對象竣贪,就會立即執(zhí)行異步操作甘桑,打印出第一行l(wèi)og艘蹋。由于是異步票灰,會繼續(xù)執(zhí)行生成Promise對象外的代碼女阀,打印出第二行l(wèi)og。異步操作成功后屑迂,打印出then里resolve回調(diào)函數(shù)里的log浸策。

由于通常異常會用下面介紹的catch方法捕捉,因此then方法的第二個(gè)參數(shù)通常會省略:

myPromise.then(function(value) {
    ... //成功
}).catch(function(error) {
    ... //失敗
});

then方法最強(qiáng)大之處在于惹盼,它內(nèi)部可以使用return或throw來實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用庸汗。使用return或throw后的返回值是一個(gè)新的Promise實(shí)例對象(注意,不是原來那個(gè)Promise實(shí)例對象):

var myPromise = new Promise(function(resolve, reject) {
    resolve(1);
});

myPromise.then(function(value) {
    console.log("第" + value + "一次異步操作成功");  //第1次異步操作成功
    return value+1;
}).then(function(value) {
    console.log("第" + value + "一次異步操作成功");  //第2次異步操作成功
});

myPromise.then(function(value) {
   console.log("第" + value + "一次異步操作成功");   //第1次異步操作成功
});

上面代碼中手报,myPromise對象第一次調(diào)用then時(shí)蚯舱,value為1,打印出log后return出一個(gè)匿名Promise對象掩蛤。

你可能會疑惑枉昏,看代碼return value+1;(相當(dāng)于return 2;)只是返回了一個(gè)數(shù)字,并沒有返回Promise對象呀揍鸟。其實(shí)它會隱式調(diào)用下面介紹的Promise.resolve靜態(tài)方法將轉(zhuǎn)為一個(gè)Promise對象兄裂。具體怎么轉(zhuǎn)的,請參照下面介紹的Promise.resolve靜態(tài)方法。

如果then方法內(nèi)部不是return晰奖,而是throw谈撒,會隱式調(diào)用下面介紹的Promise.reject靜態(tài)方法返回一個(gè)Rejected狀態(tài)的Promise對象。

由于then里用return或throw返回的是另一個(gè)Promise對象匾南,這就實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用啃匿。

代碼中返回得到的匿名Promise對象,由于狀態(tài)是Resolved午衰,立即調(diào)用鏈?zhǔn)街械诙€(gè)then方法立宜,value為2。(如果匿名Pormise對象里有異步API臊岸,那仍舊會等操作成功或失敗后橙数,再觸發(fā)調(diào)用鏈中的then方法)

最后一次then方法由myPromise對象調(diào)用,因?yàn)镻romise對象的狀態(tài)改變后就固定了帅戒,永遠(yuǎn)不會再次改變灯帮,所以value值仍舊為1。

一個(gè)常見的錯(cuò)誤是逻住,忘記寫return或throw钟哥,但卻對then方法使用鏈?zhǔn)秸{(diào)用。如上例中將return語句刪掉:

myPromise.then(function(value) {
    console.log("第" + value + "一次異步操作成功");  //第1次異步操作成功
}).then(function(value) {
    console.log("第" + value + "一次異步操作成功");  //第undefined次異步操作成功
});

結(jié)果發(fā)現(xiàn)鏈?zhǔn)秸{(diào)用后瞎访,下一個(gè)then方法得到的是undefined腻贰。因?yàn)槿绻伙@式寫return語句的話,JS里的函數(shù)會自動 return undefined扒秸。這樣就相當(dāng)于調(diào)用下面介紹的Promise.resolve(undefined)播演。雖然瀏覽器覺得這段代碼合法,不會報(bào)錯(cuò)伴奥,但通常來說這不是你期待的結(jié)果写烤。因此推薦:

確保處于調(diào)用鏈中間的then方法內(nèi)部永遠(yuǎn)顯式的調(diào)用return或者throw。

catch方法

Promise.prototype.catch()同樣是實(shí)例方法拾徙,需要經(jīng)由Promise實(shí)例對象來調(diào)用洲炊,用于Promise實(shí)例對象狀態(tài)為Rejected的后續(xù)處理,即異常處理尼啡。函數(shù)聲明:p.catch(reject);暂衡。參照MDN

catch方法本質(zhì)上等價(jià)于then(null, reject),因此參數(shù)reject在上面介紹過了玄叠,是一個(gè)回調(diào)函數(shù)古徒,它的參數(shù)就是Promise對象狀態(tài)變?yōu)镽ejected后,傳遞來的錯(cuò)誤信息读恃。

例如:

var myPromise = new Promise(function(resolve, reject) {
    throw "異步操作失敗";
    resolve("成功");
});

myPromise.then(function(value) {
    console.log(value);
}).catch(function(e) {
    console.log(e);
});
//異步操作失敗

異步操作時(shí)throw了異常,導(dǎo)致myPromise狀態(tài)變成Rejected。由于then里未定義第二個(gè)可選的reject回調(diào)函數(shù)寺惫,所以跳過then方法疹吃,進(jìn)入catch,打印出log西雀。

有個(gè)細(xì)節(jié)要注意萨驶,上面異步操作里throw和resolve語句別寫反了,寫反了是不會捕捉異常的:

var myPromise = new Promise(function(resolve, reject) {
    resolve("成功");  //throw和resovle的順序?qū)懛戳?    throw "異步操作失敗";
});

myPromise.then(function(value) {
    console.log(value);
}).catch(function(e) {
    console.log(e);
});
//成功

上面代碼未能捕獲異常艇肴,并不是throw語句未被執(zhí)行腔呜,throw語句確實(shí)被執(zhí)行了。但由于已經(jīng)將狀態(tài)改為Resolved了再悼,Promise對象的狀態(tài)一旦改變將永不再變核畴。因此不會進(jìn)入catch語句里。即throw異常的話冲九,要保證Promise對象的狀態(tài)為Rejected谤草,否則即使throw了異常,也沒有回調(diào)函數(shù)能捕捉該異常莺奸。

那如果catch之前的then里也定義了第二個(gè)可選的reject回調(diào)函數(shù)參數(shù)呢丑孩?究竟是then的reject捕捉還是catch捕捉?其實(shí)明白了catch是then(null, reject)的別名灭贷,就能推導(dǎo)出温学,應(yīng)該被then的reject捕捉:

myPromise.then(function(value) {
    console.log(value);
},function(e) {
    console.log(e + ",由then的reject回調(diào)函數(shù)捕捉");
}).catch(function(e) {
    console.log(e);
});
//異步操作失敗甚疟,由then的reject回調(diào)函數(shù)捕捉

上面代碼等價(jià)于:

myPromise.then(function(value) {
    console.log(value);
},function(e) {
    console.log(e + "仗岖,由then的reject回調(diào)函數(shù)捕捉");
}).then(null, function(e) {
    console.log(e);
});
//異步操作失敗,由then的reject回調(diào)函數(shù)捕捉

從結(jié)果可以看出古拴,catch等價(jià)于then(null, reject)箩帚,相當(dāng)于調(diào)用鏈上全是then,自然是被第一個(gè)定義了reject回調(diào)的then捕捉黄痪。

當(dāng)然使用catch看起來代碼更清晰紧帕,所以通常會省略then方法的第二個(gè)參數(shù)reject回調(diào),用catch來捕捉異常桅打。

then的返回值是一個(gè)全新的Promise對象是嗜,catch也不例外,同樣適用于鏈?zhǔn)秸{(diào)用挺尾。雖然通常catch都放在鏈?zhǔn)秸{(diào)用的最后鹅搪,但沒人強(qiáng)制規(guī)定必須這么做,catch后面完全可以繼續(xù)接then遭铺。同樣的丽柿,如果catch未放在調(diào)用鏈最后恢准,而是在調(diào)用鏈中間的話,別忘了顯示地寫上return或throw語句甫题。

另外Promise對象的異常同樣具有冒泡特性馁筐,會一直向后傳遞,直到被捕捉到為止坠非。因此then方法中拋出的異常敏沉,也能被catch捕捉。因?yàn)檎{(diào)用鏈里then拋出異常相當(dāng)于將匿名Promise對象的狀態(tài)改成Rejected炎码,這樣異常在調(diào)用鏈上傳遞盟迟,直到異常被捕捉到:

var myPromise = new Promise(function(resolve, reject) {
    resolve("成功");
});

myPromise.then(function(value) {
    console.log(value);
    throw "then的resolve里拋出異常";
}).catch(function(e) {
    console.log(e);
});
//成功
//then的resolve里拋出異常

上面代碼中,異常操作成功潦闲,打印出log攒菠。但在then的resolve回調(diào)函數(shù)里又throw出了異常,該異常會被then返回的匿名Promise對象繼續(xù)傳遞給catch矫钓。如果catch之前有多個(gè)then要尔,無論哪里拋出異常,最終都會被catch捕捉到新娜。

那如果異常未被捕捉到會怎么樣呢赵辕?例如myPromise.then(…).catch(…).then(…);。最后的then里拋出異常概龄,或者catch里拋出異常(沒錯(cuò)还惠,catch等價(jià)于then(null, reject),自然在catch方法之中私杜,也能再拋出異常)蚕键,會怎么樣呢?

這就是Promise異常的冒泡和傳統(tǒng)的try/catch代碼塊的異常冒泡的區(qū)別:如果Promise異常最終未被捕捉到衰粹,不會繼續(xù)傳遞到外層代碼锣光,即不會有任何反應(yīng):

var myPromise = new Promise(function(resolve, reject) {
    resolve(x);     //error,x未定義
});

myPromise.then(function(e) {
    console.log('成功');}
);

上面代碼沒有catch語句铝耻,因此異常未被捕捉到誊爹,外層代碼也沒有任何反應(yīng),仿佛什么都沒發(fā)生(注意瓢捉,試下來Chrome會拋出x未定義的錯(cuò)誤)频丘。忘記在最后加上catch,會導(dǎo)致珍貴的異常信息丟失泡态,這對調(diào)試來說是異常痛苦的經(jīng)歷搂漠。要記住,通常程序員對自己寫的代碼會很自信某弦,覺得不會拋出一個(gè) error桐汤,但現(xiàn)實(shí)不同環(huán)境下而克,相同代碼的執(zhí)行結(jié)果會大相徑庭。所以為了將來調(diào)試方便惊科,建議:

在promise調(diào)用鏈最后添加catch拍摇。

myPromise().then(function () {
    …
}).then(function () {
    …
}).catch(console.log.bind(console));

靜態(tài)方法(resolve亮钦,reject馆截,all,race)

這里介紹的4個(gè)都是靜態(tài)方法蜂莉,非實(shí)例方法蜡娶,用Promise對象是無法調(diào)用的。

Promise.resolve將對象轉(zhuǎn)為Promise對象映穗。函數(shù)聲明:Promise.resolve(value);窖张。參照MDN

參數(shù)是一個(gè)對象,轉(zhuǎn)換成Promise對象返回出去蚁滋。你可以用該方法將jQuery的Deferred對象轉(zhuǎn)換成一個(gè)Promise對象:

var myPromise = Promise.resolve($.ajax('/xx.json'));

參數(shù)分4種情況:

1.如果參數(shù)對象本身就是Promise對象宿接,那就將該對象原封不動返回。

2.如果參數(shù)對象本身是thenable對象(即對象有then方法)辕录,轉(zhuǎn)為Promise對象并返回后睦霎,立即執(zhí)行thenable對象的then方法。例如:

var myPromise = Promise.resolve({ 
    then: function(resolve) { 
      resolve("成功"); 
    }
});
console.log(myPromise instanceof Promise);

myPromise.then(function(v) {
    console.log(v);
});
//true
//成功

上面代碼中走诞,Promise.resolve的參數(shù)里有then方法副女,因此myPromise對象生成后,立即執(zhí)行then方法蚣旱,根據(jù)異步操作的結(jié)果碑幅,調(diào)用then里resolve/reject回調(diào)函數(shù)

3.如果參數(shù)對象本身不是thenable對象(即對象沒有then方法),例如一個(gè)數(shù)字?jǐn)?shù)組等塞绿,那會返回一個(gè)狀態(tài)固定為Resolved的全新的Promise對象:

var myPromise = Promise.resolve([1,2,3]);
myPromise.then(function(v) {
    console.log(v);  
});
//[1,2,3]

由于數(shù)組不是具有then方法的對象沟涨,返回Promise實(shí)例的狀態(tài)固定為Resolved,觸發(fā)調(diào)用then方法异吻。原先的參數(shù)對象裹赴,會被傳給then方法的resolve回調(diào)函數(shù)做為參數(shù)。

為什么呢涧黄?因?yàn)镻romise.resolve([1,2,3]);等價(jià)于:

new Promise(function(resolve, reject) {
    resolve([1,2,3]);
});

4.如果沒有參數(shù)顿肺,那會返回一個(gè)狀態(tài)固定為Resolved的全新的空的Promise對象授滓。雖然瀏覽器不會報(bào)錯(cuò),但也沒什么卵用。

Promise.reject將對象轉(zhuǎn)換成一個(gè)狀態(tài)為Rejected的全新的Promise對象躯肌。其他同Promise.resolve,不贅述纫骑。函數(shù)聲明:Promise.reject(error);。參照MDN

該方法常用于調(diào)試嫉你,顯示棧信息:

Promise.reject(new Error("fail")).then(function(error) {
    // not called
}, function(error) {
    console.log(error);   // Stacktrace
});

Promise.all方法用于將多個(gè)Promise實(shí)例對象,包裝成一個(gè)新的Promise實(shí)例對象躏惋。函數(shù)聲明:Promise.all(iterable);幽污。參照MDN

參數(shù)是一個(gè)Promise對象數(shù)組。例如:

var p = Promise.all([p1, p2, p3]);

p1簿姨,p2距误,p3都是Promise對象,如果不是扁位,就會先調(diào)用Promise.resolve方法准潭,將參數(shù)轉(zhuǎn)為Promise對象。(Promise.all方法的參數(shù)可以不是數(shù)組域仇,但必須具有Iterator接口)

返回值是一個(gè)全新的Promise對象刑然。它的狀態(tài)由參數(shù)的狀態(tài)決定:

當(dāng)p1,p2暇务,p3的狀態(tài)都變成Resolved泼掠,p的狀態(tài)才會變成Resolved。然后p1垦细,p2择镇,p3的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)蝠检。

只要p1沐鼠,p2,p3中有一個(gè)變成Rejected叹谁,p的狀態(tài)就變成Rejected饲梭,此時(shí)第一個(gè)Rejected的實(shí)例的返回值,會傳遞給p的回調(diào)函數(shù)焰檩。

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, "foo");
}); 
Promise.all([p1, p2, p3]).then(values => { 
    console.log(values);    //1秒后顯示[3, 1337, "foo"] 
});

all方法用于哪里呢憔涉?一個(gè)非常常見場景是:程序員想用forEach,for析苫,while等循環(huán)來處理他們的異步結(jié)果兜叨。例如刪除所有臨時(shí)文件:

db.allDocs({include_docs: true}).then(function (result) {
    result.rows.forEach(function (row) {        //用forEach刪除所有臨時(shí)文件
        db.remove(row.doc);
    });
}).then(function () {
    //我認(rèn)為執(zhí)行到這里時(shí),所有臨時(shí)文件都已經(jīng)被刪了衩侥。但是我錯(cuò)了…
});

上面代碼国旷,第一個(gè)then的回調(diào)里,期望用循環(huán)刪除所有臨時(shí)文件后茫死,再進(jìn)入第二個(gè)then跪但。但事實(shí)上,由于第一個(gè)then里沒有return語句(原因是刪除文件是個(gè)異步動作峦萎,如果寫了return語句屡久,刪文件執(zhí)行之前就會return)忆首,所以返回的是undefined。因此第二個(gè)then并不是在所有文件均被刪除后才執(zhí)行的被环,實(shí)際上糙及,第二個(gè)then的執(zhí)行不會有任何延遲,會被立即執(zhí)行筛欢。

如果第二個(gè)then里并不需要對臨時(shí)文件進(jìn)行任何操作浸锨,那這個(gè)bug可能會隱藏的很深。但如果在第二個(gè)then里刷新UI頁面悴能,那時(shí)不時(shí)UI頁面上會出現(xiàn)一些還來不及刪掉的文件揣钦,這取決于刪文件的速度。程序員都知道漠酿,最討厭的不是出bug,而是出了看運(yùn)氣才能再現(xiàn)的bug谎亩。

所以應(yīng)該用Promise.all來寫這種需要循環(huán)處理異步的操作炒嘲,可以理解為異步的循環(huán)。

db.allDocs({include_docs: true}).then(function (result) {
    return Promise.all(result.rows.map(function (row) {
        return db.remove(row.doc);
    }));
}).then(function (arrayObject) {
    //我承諾執(zhí)行到這里時(shí)匈庭,所有臨時(shí)文件都已經(jīng)被刪掉了夫凸。
})

Promise.race方法和all方法類似,函數(shù)聲明:Promise.race(iterable);阱持,參照MDN

var p = Promise.race([p1,p2,p3]);

參數(shù)p1夭拌,p2,p3都是Promise對象衷咽,如果不是鸽扁,就會先調(diào)用Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise對象镶骗,這點(diǎn)和all相同桶现,不贅述。

和Promise.all的區(qū)別是鼎姊,race表示競爭骡和。只要任意一個(gè)參數(shù)對象的狀態(tài)發(fā)生改變,就會立即返回一個(gè)相同狀態(tài)的Promise對象相寇。

簡單地說:all里全為Resolved才返回Resolved慰于,有一個(gè)為Rejected就返回Rejected。race里有一個(gè)為Resolved/Rejected就返回Resolved/Rejected

race常用于競爭異步執(zhí)行的結(jié)果場景唤衫,例如:指定時(shí)間內(nèi)沒有獲得結(jié)果就Rejected:

var p = Promise.race([
    fetch('/resource'),
    new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
    })
]);
p.then(response => console.log(response))
  .catch(error => console.log(error));

上面代碼中婆赠,如果5秒之內(nèi)無法fetch到數(shù)據(jù),p的狀態(tài)就會變?yōu)镽ejected战授,從而觸發(fā)catch方法指定的回調(diào)函數(shù)页藻。

例子

例如我們要做兩次異步操作(用最簡單的異步操作setTimeout為例)桨嫁,第一次異步操作成功后執(zhí)行第二次異步操作:

function delay(time, callback){
    setTimeout(function(){
        callback("sleep "+time);
    },time);
}   

delay(1000,function(msg){
    console.log(msg);
    delay(2000,function(msg){
        console.log(msg);
    });
});
//1秒后打印出:sleep 1000
//再過2秒打印出:sleep 2000

上面用回調(diào)嵌套很容易實(shí)現(xiàn),但也發(fā)現(xiàn)才兩層(還沒加上異常處理)份帐,代碼就比較難看了璃吧。改成Promise試試:

function delay(time, callback){
    setTimeout(function(){
        callback("sleep "+time);
    },time);
} 

var p = new Promise(function (resolve, reject) {
    delay(1000, resolve);
});

p.then(function(value) {
    console.log(value);
    return new Promise(function (resolve, reject) {
        delay(2000, resolve);
    });
}).then(function(value) {
    console.log(value);
}).catch(console.log.bind(console));

因?yàn)椴艃蓚€(gè)異步操作,所以代碼行數(shù)上看废境,優(yōu)勢不明顯畜挨。優(yōu)勢在于Promise讓代碼的邏輯流程變得更清晰。先執(zhí)行第一次異步操作噩凹,成功后進(jìn)入then巴元,打印出結(jié)果,并執(zhí)行第二次異步操作驮宴,成功后進(jìn)入then逮刨,打印出結(jié)果。

總結(jié)

ES6 Promise的實(shí)現(xiàn)嚴(yán)格遵循了Promise/A+規(guī)范堵泽,用Promise可以寫出更可讀的異步編程代碼修己,避免了回調(diào)地獄。需要注意的是迎罗,由于歷史原因睬愤,有的庫,例如jQuery的Deferred就不是Promise/ A+的規(guī)范纹安,它是個(gè)工廠類尤辱,返回的是內(nèi)部構(gòu)建的deferred對象。本篇篇幅不夠去介紹Deferred和Promise的區(qū)別厢岂,但ES6的Promise完全可以取代Deferred光督。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咪笑,隨后出現(xiàn)的幾起案子可帽,更是在濱河造成了極大的恐慌,老刑警劉巖窗怒,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件映跟,死亡現(xiàn)場離奇詭異,居然都是意外死亡扬虚,警方通過查閱死者的電腦和手機(jī)努隙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辜昵,“玉大人荸镊,你說我怎么就攤上這事。” “怎么了躬存?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵张惹,是天一觀的道長。 經(jīng)常有香客問我岭洲,道長宛逗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任盾剩,我火速辦了婚禮雷激,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘告私。我一直安慰自己屎暇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布驻粟。 她就那樣靜靜地躺著根悼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪格嗅。 梳的紋絲不亂的頭發(fā)上番挺,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音屯掖,去河邊找鬼。 笑死襟衰,一個(gè)胖子當(dāng)著我的面吹牛贴铜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瀑晒,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼绍坝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了苔悦?” 一聲冷哼從身側(cè)響起轩褐,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玖详,沒想到半個(gè)月后把介,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蟋座,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年拗踢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片向臀。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巢墅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情君纫,我是刑警寧澤驯遇,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站蓄髓,受9級特大地震影響叉庐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜双吆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一眨唬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧好乐,春花似錦匾竿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至反璃,卻和暖如春昵慌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淮蜈。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工斋攀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梧田。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓淳蔼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親裁眯。 傳聞我的和親對象是個(gè)殘疾皇子鹉梨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗(yàn)的人,如果你還沒有使用過Promise穿稳,這篇文章可能不適合你存皂,...
    HZ充電大喵閱讀 7,296評論 6 19
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,164評論 0 16
  • 00、前言Promise 是異步編程的一種解決方案埋虹,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大猜憎。它由社區(qū)...
    夜幕小草閱讀 2,127評論 0 12
  • Promiese 簡單說就是一個(gè)容器,里面保存著某個(gè)未來才會結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果搔课,語法上說胰柑,Pr...
    雨飛飛雨閱讀 3,348評論 0 19
  • 貴義職教開會內(nèi)容宿舍:班級統(tǒng)一 學(xué)校培養(yǎng):基本素質(zhì) 公司專業(yè)知識 學(xué)校7S標(biāo)準(zhǔn)化:整理截亦、整頓、清潔柬讨、清掃崩瓤、...
    付佳婕閱讀 435評論 0 0