Javascript 中的Promise
一,什么是 Promise
Promise 是异步编程的一种解决方案---回调函数中的回调地狱(callback hell)。ES6 将其写进了语言标准,统一了用法。简单的说 Promise就是一个容器,里面保存着某个未来才会结束的事件。
一个 Promise有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
codeacademy中的一个洗碗机的例子就很形象:

- Pending: 洗碗机在工作,但是没有完成清洗流程
- Fulfilled: 洗碗机完成了所有清洗流程,并且装满了干净的餐具
- Rejected: 洗碗机工作过程中出了问题(比如没加洗洁精),结果里面都是不干净的餐具
使用Promise前先看个简单例子热身:
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,调用resolve(...), 当异步代码失败时就会调用reject(...)
//本例使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型
console.log("Yay! " + successMessage);
});
二,Promise的使用
1,使用约定
- 在 本轮事件循环(event loop)运行完成之前,回调函数是不会被调用的
- 通过
then()添加的回调函数总会被调用,即便它是在异步操作完成之后才被添加的函数 - 通过多次调用
then(),可以添加多个回调函数,它们会按照插入顺序一个接一个独立执行
因此,Promise 最直接的好处就是链式调用(chaining)。

2,链式调用
在ES6之前,多重的异步操作,会导致经典的回调地狱:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
ES6之后,可以把回调绑定到被返回的 Promise 上代替以往的做法,形成一个 Promise 链:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
注意:一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。
在 ECMAScript 2017 标准的 async/await 语法糖中,使得逻辑更加的清晰
async function foo() {
try {
let result = await doSomething();
let newResult = await doSomethingElse(result);
let finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
这跟我之前使用Python中的异常捕获有着异曲同工之妙。
3,组合使用
Promise.resolve()]和 Promise.reject() 是手动创建一个已经 resolve 或者 reject 的Promise的快捷方法[Promise.all() 和 [Promise.race() 是并行运行异步操作的两个组合式工具。
比如:
Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });
发起并行操作,然后等多个操作全部结束后进行下一步操作。
4,关于时序
即使是一个已经变成 resolve 状态的 Promise,传递给 then() 的函数也总是会被异步调用。传递到 then() 中的函数被置入了一个微任务队列,而不是立即执行,这意味着它是在 JavaScript 事件队列的所有运行时结束了,事件队列被清空之后,才开始执行:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); wait().then(() => console.log(4)); Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); console.log(1); // 1, 2, 3, 4
三,Promise的特点小结
- 对象的状态不受外界影响。
Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。