說來用Promise也有一段時間了,一直把它當(dāng)成一個callback hell的解決方案在用,使用它的時候,并沒有真正的了解它。最近同事給我推薦了一篇文章:We have a problem with Promise, 才發(fā)現(xiàn)Promise的門道還是很多的,讀過這篇文章后,我重新整理了一下對Promise的認(rèn)知,英文還可以的同學(xué)一定要看原文。
Promise的基本用法:
常見的Promise用法如下:
somePromise()
.then(function () {
// I'm inside a then() function!
})
.then(function (previousResult) { })
.catch(error) {errorHandler};
在then()方法中,可能出現(xiàn)三種情況:
- 返回一個新的Promise
somePromise().then(function () {
return anotherPromise();
});
通過then()方法,把多個異步操作順序的執(zhí)行下去。
- 返回一個值
somePromise().then(function () {
return somevalue;
});
通過這個方式可以把同步操作插入到異步操作流中,以統(tǒng)一的形式來管理操作流的時序。
- Throw一個異常
somePromise().then(function () {
throw new Error('You bad bad!');
});
不管是前面是同步計算值,還是異步Promise, 只要throw出異常,都會被后面的catch抓住,進(jìn)行相應(yīng)的處理。
上面的一切都看起來非常美好,非常簡單,但是你真的理解Promise了嗎?
理解Promise執(zhí)行的時序
Promise對程序來說,主要的作用是:有異步操作時,能夠讓程序仍然具有同步操作時的時序,return, throw exception等基礎(chǔ)程序功能。因此,當(dāng)使用Promise最重要的就是清楚自己寫出來的代碼在執(zhí)行時的時序,以及前一步的結(jié)果如何傳遞給后續(xù)的操作。
假設(shè)doSomething*()都會返回一個Promise,你是否能清楚的描述出下面的幾段代碼的時序,以及每個方法接收的參數(shù)?
Code 1:
doSomething()
.then(function () { return doSomethingElse();})
.then(finalHandler);
Code 2:
doSomething()
.then(function () { doSomethingElse();})
.then(finalHandler);
Code 3:
doSomething()
.then(doSomethingElse())
.then(finalHandler);
Code 4:
doSomething()
.then(doSomethingElse)
.then(finalHandler);
Code 5:
Promise.all([
doSomethingOne(),
doSomethingTwo(),
doSomethingThree()
])
.then(function (previusResult) {
return doSomethingElse(previusResult);
})
.then(finalHandler);
?```
答案:
Code 1: doSomethingElse會等待doSomething會先執(zhí)行到返回一個Promise,
但是因為doSomethingElse的wrapper function沒有接收上一步的返回值。
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|
Code 2: 沒有return語句, 默認(rèn)返回undefined, 下一步執(zhí)行不會等待上一步執(zhí)行完成。
doSomething
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(undefined)
|------------------|
Code 3: 第一個then()方法內(nèi)部的沒有function Wrapper, 在構(gòu)建Promise時就會執(zhí)行doSomethingElse(),
并返回結(jié)果給下一步finalHandler。
doSomething
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|
Code 4: Promise之間可以在then()方法之間直接連接,這樣可以節(jié)省不少代碼。
doSomething
|-----------------|
doSomethingElse(resultOfDoSomething)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|```
Code 5: Promise.all會并發(fā)的執(zhí)行參數(shù)中所有的Promise, 并等待所有的Promise的返回結(jié)果,然后封裝為Array返回給后續(xù)的操作。
doSomethingOne
|-----------------|
doSomethingTwo
|-----------------|
doSomethingThree
|-----------------|
doSomethingElse([resultOfOne,resultOfTwo, resultOfThree])
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|```
###小測驗
**問題一:**下面的代碼會有什么問題?
```?{JAVASCRIPT}
DialogActions.hide()
.then(() => {
Actions.deletePartOfA()
.then(() =>{
Actions.retrieveA()
});
這段代碼原本的目的是刪除一個Resource A的某個部分之后,重新請求Resource A以保證本地和遠(yuǎn)端的Resource同步,但是Actions.deletePartOfA() 沒有return,最后Actions.retrieveA()和Actions.deletePartOfA()會同時執(zhí)行,因此最后Actions.retrieveA()得到的Resource A并沒有刪除那個部分。
問題二:下面兩份代碼有什么不同?
Code 1:
Promise.resolve()
.then(() => { throw new Error('Test')})
.catch((e) => { console.error(e)});
Code 2:
Promise.resolve()
.then(() => { throw new Error('Test')}, (e) =>{ console.error(e);})
Code 2無法打印出error, 因為當(dāng)RejectHandler無法捕獲自己平級的ResolveHandler拋出的異常。
問題三:下面的兩份代碼會輸出什么結(jié)果?
Code 1:
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function (result) { console.log(result);});
Code 2:
Promise.resolve('foo')
.then(function () { return Promise.resolve('bar');})
.then(function (result) { console.log(result);});
Code 1會輸出'foo', Code 2會輸出 'bar'。當(dāng)在then()方法中直接放置一個Promise定義(沒有function wrapper)時,效果等同then(null),這種情況下,Promise的結(jié)果會穿透這個then(null).
問題四:下面的兩份代碼會輸出什么結(jié)果?
function throwError() { throw new Error('Test');};
Code 1:
Promise.resolve(throwError())
.catch(error => { console.log(error); });
Code 2:
Promise.resolve()
.then(() =>{ throwError() })
.catch(error => { console.log(error); });```
Code 1不能捕獲異常,Code 2能夠正常捕獲異常, Code1在構(gòu)建Promise時就拋出異常,這時catch語句還沒準(zhǔn)備好接收異常。
###Promise推薦實踐
1. 每個then()方法都使用return語句。
2. 每個then()方法中都使用function wrapper。
3. 每個Promise最后都接一個catch幫助定位錯誤。
4. 使用catch捕獲異常而非rejectHandler。
5. 使用Promise.resolve()創(chuàng)建Promise