基本使用
以ES6的 promise 为准。
在处理一个异步操作时,如果希望通过promise来实现,我们只需要两步:
- 封装一个promise对象
function someSyncFn() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 2s"); resolve(2000); }, 2000); }) }
- 调用then方法
someSyncFn().then(function(value) { console.log("getValue:" + value); });
单从上述方法其实并不能看出来promise的优势,一个callback改成了一个then而已,我们看多个异步操作的情况:
function someSyncFn1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 2s"); resolve(2000); }, 2000); }) } function someSyncFn2(value1) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 5s"); resolve(3000 + value1); }, 3000); }) } someSyncFn1().then(someSyncFn2).then(function(value) { console.log("getValue:" + value); });我们发现,当链式异步并且存在先后顺序的时候,通过promise的确能够写出更简洁的代码,而且并不需要很多api,和单个异步一样,只需要两步,封装promise对象和调用then方法。 对于平行的异步调用,promise提供了 promise.all
function someSyncFn1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 2s"); resolve(2000); }, 2000); }) } function someSyncFn2() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 5s"); resolve(3000); }, 3000); }) } Promise.all([someSyncFn1(), someSyncFn2()]).then(function(results) { console.log(results); });上面的 results会顺序保存 两个异步的结果。 # 问题 从上面的基本用法,我们可能会觉得 promise 非常简单,但是我经常看到各种奇怪的写法,有好的也有反模式,我发现并不能弄懂它们的区别。比如: + then中直接调用方法
function someSyncFn1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 2s"); resolve(2000); }, 2000); }) } function someSyncFn2(value1) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 5s"); resolve(3000 + value1); }, 3000); }) } someSyncFn1().then(function(value) { someSyncFn2(value); // 在then中直接调用 第二个异步函数 }).then(function(value) { console.log("getValue:" + value); });
- then中返回一个值
function someSyncFn1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("after 2s"); resolve(2000); }, 2000); }) } someSyncFn1().then(function(value) { console.log('async console'); return value; //在then中返回值 }).then(function(value) { console.log("getValue:" + value); });
一个异步之后需要多个回调的两种写法的区别:
//写法1 someSyncFn1().then(callback1).then(callback2); //写法2 var promiseRt = someSyncFn1(); promiseRt.then(callback1); promiseRt.then(callback2); //是否写法1 是串行,写法2是 并行呢?
以及更经典的,下面四种写法区别在哪里:
doSomething().then(function () {
return doSomethingElse();
});
doSomethin().then(functiuoin () {
doSomethingElse();
});
doSomething().then(doSomethingElse());
doSomething().then(doSomethingElse);
我们发现上面的执行结果乱七八糟,如果不能了解 promise内部执行流程,很难回答上面的问题,于是我们看一下官方规范到底是怎样的。
promiseA+规范
promiseA+规范从规范的角度解释了promise的行为,其规范并不长,由于已经有一篇质量不错的翻译,这里就直接贴上引用:
Promise/A+规范
在规范中值得关注的几点:
then 必须返回一个promise.
promise2 = promise1.then(onFulfilled, onRejected);
- 如果onFulfilled 或 onRejected 返回了值x, 则执行Promise 解析流程[Resolve].
- 如果onFulfilled 或 onRejected抛出了异常e, 则promise2应当以e为reason被拒绝。
- 如果 onFulfilled 不是一个函数且promise1已经fulfilled,则promise2必须以promise1的值fulfilled.
- 如果 OnReject 不是一个函数且promise1已经rejected, 则promise2必须以相同的reason被拒绝.
以及解析流程
由此可见,不管给promise.then()传递怎样的实参,最终都会返回一个新的promise对象,而接下来如果链式调用then方法实际上是绑定在了这个新生成的promise对象上面。
我们日常书写的存在疑虑的部分大都是在 then
的返回值上面,可能会有几种写法:
- return undefined或者一个值
- return 一个promise
- throw 一个error
return undefined或者一个值的时候,新的promise对象将会以x为值 fulfill 从而直接触发了接下来的then方法并且把 这个值传递过去。
return 一个promise后,新生成的promise2将会等待这个返回的promise状态从 pending-resolve或者reject的过程,以它的状态来 fulfill或者rejects 自己的回调。 如果promise已经成功或者失败了,它也直接使用return的promise的值来触发自己的then方法。
分析
对于之前的问题:
1 then中直接调用方法,不管这个方法是同步的还是异步的,只要其没有显示的使用return,js默认return了undefined,按照上述规范描述,将会直接触发接下来的then方法,并且将undefined作为值传递下去。
2 then中return了一个值,同1.
3 写法1和写法2都是并行,因为写法1属于返回一个 undefined的情况,链式then方法都会被执行,并不会等待。另外,写法1的then方法是挂在不同的promise对象上,写法2是挂在同一个上面。