说来用Promise也有一段时间了,一直把它当成一个callback hell的解决方案在用,使用它的时候,并没有真正的了解它。最近同事给我推荐了一篇文章:We have a problem with Promise, 才发现Promise的门道还是很多的,读过这篇文章后,我重新整理了一下对Promise的认知,英文还可以的同学一定要看原文。
Promise的基本用法:
常见的Promise用法如下:
somePromise()
.then(function () {
// I'm inside a then() function!
})
.then(function (previousResult) { })
.catch(error) {errorHandler};
在then()方法中,可能出现三种情况:
- 返回一个新的Promise
somePromise().then(function () {
return anotherPromise();
});
通过then()方法,把多个异步操作顺序的执行下去。
- 返回一个值
somePromise().then(function () {
return somevalue;
});
通过这个方式可以把同步操作插入到异步操作流中,以统一的形式来管理操作流的时序。
- Throw一个异常
somePromise().then(function () {
throw new Error('You bad bad!');
});
不管是前面是同步计算值,还是异步Promise, 只要throw出异常,都会被后面的catch抓住,进行相应的处理。
上面的一切都看起来非常美好,非常简单,但是你真的理解Promise了吗?
理解Promise执行的时序
Promise对程序来说,主要的作用是:有异步操作时,能够让程序仍然具有同步操作时的时序,return, throw exception等基础程序功能。因此,当使用Promise最重要的就是清楚自己写出来的代码在执行时的时序,以及前一步的结果如何传递给后续的操作。
假设doSomething*()都会返回一个Promise,你是否能清楚的描述出下面的几段代码的时序,以及每个方法接收的参数?
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会先执行到返回一个Promise,
但是因为doSomethingElse的wrapper function没有接收上一步的返回值。
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|
Code 2: 没有return语句, 默认返回undefined, 下一步执行不会等待上一步执行完成。
doSomething
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(undefined)
|------------------|
Code 3: 第一个then()方法内部的没有function Wrapper, 在构建Promise时就会执行doSomethingElse(),
并返回结果给下一步finalHandler。
doSomething
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|
Code 4: Promise之间可以在then()方法之间直接连接,这样可以节省不少代码。
doSomething
|-----------------|
doSomethingElse(resultOfDoSomething)
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|```
Code 5: Promise.all会并发的执行参数中所有的Promise, 并等待所有的Promise的返回结果,然后封装为Array返回给后续的操作。
doSomethingOne
|-----------------|
doSomethingTwo
|-----------------|
doSomethingThree
|-----------------|
doSomethingElse([resultOfOne,resultOfTwo, resultOfThree])
|------------------|
finalHandler(resultOfDoSomethingElse)
|------------------|```
###小测验
**问题一:**下面的代码会有什么问题?
```�{JAVASCRIPT}
DialogActions.hide()
.then(() => {
Actions.deletePartOfA()
.then(() =>{
Actions.retrieveA()
});
这段代码原本的目的是删除一个Resource A的某个部分之后,重新请求Resource A以保证本地和远端的Resource同步,但是Actions.deletePartOfA() 没有return,最后Actions.retrieveA()和Actions.deletePartOfA()会同时执行,因此最后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, 因为当RejectHandler无法捕获自己平级的ResolveHandler抛出的异常。
问题三:下面的两份代码会输出什么结果?
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'。当在then()方法中直接放置一个Promise定义(没有function wrapper)时,效果等同then(null),这种情况下,Promise的结果会穿透这个then(null).
问题四:下面的两份代码会输出什么结果?
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在构建Promise时就抛出异常,这时catch语句还没准备好接收异常。
###Promise推荐实践
1. 每个then()方法都使用return语句。
2. 每个then()方法中都使用function wrapper。
3. 每个Promise最后都接一个catch帮助定位错误。
4. 使用catch捕获异常而非rejectHandler。
5. 使用Promise.resolve()创建Promise