JavaScript Promise

Promise是什么

Promise是抽象异步处理对象以及对其进行各种操作的组件。 —— azu 《JavaScript Promise迷你书》

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
——阮一峰 《ECMAScript 6 入门 Promise对象》


Promise对象创建

  • 创建promise对象,将异步处理包裹起来,并且返回一个对象

    1
    var promise = new Promise(fn)
  • fn中指定异步处理

    • 如果处理正常,调用resolve(处理结果值)
    • 如果处理错误,调用reject(Error对象)

fn是一个函数,可以有两个参数,resolve,reject;

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function asyncFunction() {
//创建一个Promise
//将异步处理包裹起来,传入resolve和reject
return new Promise(function (resolve, reject) {
setTimeout(function () {
//处理正常,调用resolve,把值('Async Hello world')传递给.then()方法
resolve('Async Hello world');
}, 16);
});
}
//.then()方法中输出了resolve传过来的值
asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}).catch(function (error) {
console.log(error);
});


Promise状态

Promise的状态有三种:

  • Pending
    不是Fulfilled也不是Rejected;为Promise对象刚创建后尚未被转换前的状态
  • Fullfilled
    调用resolve后进入的状态,此时可以调用onFulfilled回调函数
  • Rejected
  • 调用reject后进入的状态,此时可以调用onRejected回调函数

Figure 1. promise states
Figure 2. promise value flow

PS:状态一旦转换后(变为Fulfilled/Rejected),就不再改变了


处理Fulfilled和Rejected状态

在Promise对象中,可以通过resolve或reject改变Promise对象的状态,当状态转换为Fulfilled或Rejected时,可以通过promise.then(onFulfilled,onRejected)方法进行处理。onFulfilled和onRejected分别对应处理成功时的回调函数和处理失败时的回调函数。(名字可以随意起)

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function getURL(URL) {
// 创建并返回Promise对象
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
//异步处理成功是,调用resolve,传出req.responseText
if (req.status === 200) {
resolve(req.responseText);
} else { //异常时调用reject,传出Error对象
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 运行示例
var URL = "http://httpbin.org/get";
//getURL返回了一个Promise对象,并且通过resolve和reject改变了Promise对象的状态
//当处理成功(resolve),进入then()方法,value的值即为req.responseText
//当处理失败/异常时(reject),进入catch方法,error即reject传出的Error对象
getURL(URL).then(function onFulfilled(value){
console.log(value);
}).catch(function onRejected(error){
console.error(error);
});

PS: promise.catch(onRejected) 是 promise.then(onFulfilled,onRejected)的语法糖 (省略了onFullfilled回调函数。)


promise方法链(promise chain)

通过链式调用promise.then().then().catch()的形式,形成一个处理流程

方法链Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);

Figure 3. promise-then-catch-flow.js附图

链式调用时,通过在回调函数中把数据return,可以把值传递给下一个调用方法(then/catch)
return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终then 的结果都是返回一个新创建的promise对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function doubleUp(value) {
return value * 2; //return出去的值会被下一个then/catch接收
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
// 创建一个Promise对象,使其状态为Fullfied,并返回这个对象
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出现异常的时候会被调用
console.error(error);
});

promise-then-passing-value

then or catch?
1
2
3
4
5
6
7
8
9
10
11
12
function throwError(value) {
// 抛出异常
throw new Error(value);
}
// onRejected不会被调用
function badMain(onRejected) {
return Promise.resolve(42).then(throwError, onRejected);
}
// 有异常发生时onRejected会被调用
function goodMain(onRejected) {
return Promise.resolve(42).then(throwError).catch(onRejected);
}

在这个例子中,当Promise转为Fulfilled状态时,会调用throwError方法,抛出一个异常。
badMain在then方法中,传入了处理异常的onRejected.
goodMain在then后再用一个catch来捕获异常.
运行时发现,badMain的onRejected没被调用,而goodMain中的onRejected被调用了。

原因是onFulfilled中抛出的异常,会作为一个新的promise对象往下传,pormise.then(onFulfilled,onRejected)只能处理promise抛出的异常,但是不能处理onFulfilled抛出的异常。再看这张图可能更好理解:

p1.then(onFullfilled,onRejected).catch(onRejeted)
then()中的onRejected是针对p1的
catch()中的onRejected则是针对p1和p1.then()的

image


Promises API

Promise#then

promise.then(onFullfilled,onRejected)
当promise状态转为Fulfilled时调用onFulfilled,当promise状态转为Rejected时,调用onRejected

Promise#catch

promise.catch(onRejected)
等价于promise.then(undefined, onRejected),当pormise转为Rejected或者抛出异常时,调用onRejected

Promise.resolve
  • Promise.resolve(promise);
    传入pormise对象,返回的还是接收的promise对象
  • Promise.resolve(thenable);
    传入thenable类型对象,返回新的promise对象,此对象具有一个then方法
  • Promise.resolve(object);
    传入其他类型,返回一个新的promise对象
Promise.reject

Promise.reject(object)
传入的对象应该是Error类型的对象,返回一个新的promise对象。与Promise.resolve不同的是,即使传入的是一个pormise对象,返回的也是一个新的promise对象(而不是接收的pormise对象)

1
2
3
var promise = Promise.reject(new Error("bad!"));
console.log(promise === Promise.reject(promise)); // false
console.log(promise === Promise.resolve(promise)); // true

promise.all

Promise.all(promiseArray)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.reject("error");
// 全部都resolve,转为Fullfilled状态
Promise.all([p1, p2])
.then(function (results) {
console.log(results); // [1 , 2]
}).catch(function (error) {
console.log(error);
})
// 有一个转为了Rejected状态
Promise.all([p1, p2, p3])
.then(function (results) {
console.log(results);
}).catch(function (error) {
console.log(error); // error
})
});

传入一个promise数组(多个promise对象),返回一个新的pormise对象。

当promise数组中所有promise对象的状态都转为Fullfilled时,返回的是每个对象的返回值所构成的数组。

如果pormise数组中,有一个promise对象的状态转为Rejected,则Promime.all调用会马上停止,并返回一个reject的新的pormise对象。(返回一个Error对象)

Promise.all()中,pormise数组的每个pormise对象的执行时并行的

Promise.race

Promise.race(promiseArray);

1
2
3
4
5
6
var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
console.log(value); // 1
});

传入一个promise数组,返回一个新的pormise对象。

当pormise数组中,有一个pormise对象的状态转换为Fullfilled/Rejected,就会使用该pormise对象的值进行resolve或reject.( pormise.resolve()/promise.reject() )

一个对象转换状态后,不会取消其他pormise对象的执行。


参考链接

[1]. azu / liubin、kaku、honnkyou , JavaScript Promise迷你书(中文版)
[2]. 阮一峰,ECMAScript 6 入门