Promise.prototype.then()
作用是为 Promise 实例添加状态改变时的回调函数。返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });
第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
第一个回调函数返回一个Primise对象时,后一个回调函数就会等待该Promise对象状态发生变化才会被调用。
Promise.prototype.catch()
是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
catch方法会捕获 getJSON抛出的错误,也会捕获then指定的回调函数抛出的错误
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch
语句捕获。
一般来说,不要在then
方法里面定义 Reject 状态的回调函数(即then
的第二个参数),总是使用catch
方法。原因是catch方法总是可以捕获then方法中的错误,并且也更接近于同步的写法
跟传统的try/catch
代码块不同的是,如果没有使用catch
方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。
Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
一般总是建议,Promise 对象后面要跟catch
方法,这样可以处理 Promise 内部发生的错误。catch
方法返回的还是一个 Promise 对象,因此后面还可以接着调用then
方法。如果没有报错,则会跳过catch
方法。
catch
方法之中,还能再抛出错误。
Promise.prototype.finally()
用于指定不管 Promise 对象最后状态如何,都会执行的操作。
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
上面代码中,不管promise
最后的状态,在执行完then
或catch
指定的回调函数以后,都会执行finally
方法指定的回调函数。
finally
方法的回调函数不接受任何参数,里面的操作与状态无关,不依赖于 Promise 的执行结果。
finally
本质上是then
方法的特例。
finally
方法总是会返回原来的值。
finally
方法的实现:
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
Promise.all()
用于将多个 Promise 实例,包装成一个新的 Promise 实例。接受一个数组作为参数,数组成员为Promise实例。如果不是,会先调用Promise.resolve方法,将参数转为Promise实例。(Promise.all
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)
const p = Promise.all([p1, p2, p3]);
p
的状态由p1
、p2
、p3
决定,分成两种情况:
- 只有
p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。 - 只要
p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
注意,如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
Promise.race()
同样是将多个 Promise 实例,包装成一个新的 Promise 实例。参数与Promise.all
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。
const p = Promise.race([p1, p2, p3]);
只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.resolve()
将现有对象转为 Promise 对象。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
Promise.resolve
等价于下面的写法:
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
参数分成四种情况:
- 参数是一个 Promise 实例:将不做任何修改、原封不动地返回这个实例。
- 参数是一个
thenable
对象:thenable
对象指的是具有then
方法的对象,将这个对象转为 Promise 对象,然后就立即执行thenable
对象的then
方法。
- 参数不是具有
then
方法的对象,或根本就不是对象:返回一个新的 Promise 对象,状态为resolved
。 - 不带有任何参数:直接返回一个
resolved
状态的 Promise 对象。需要注意的是,立即resolve
的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。
Promise.reject()
返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) }); // 出错了
注意,Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
Promise.try()
让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API。
其他变通的两种方法如下:
方法一:async
函数。注意,async () => f()
会吃掉f()
抛出的错误,要使用primise.catch方法捕获。
const f = () => console.log('now'); (async () => f())().then(...).catch(...); console.log('next'); // now // next
方法二:new Promise()。
const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); console.log('next'); // now // next
使用Promise.try方法代替以上两种写法:
const f = () => console.log('now'); Promise.try(f); console.log('next'); // now // next
事实上,Promise.try
就是模拟try
代码块,就像promise.catch
模拟的是catch
代码块。
Promise.try(database.users.get({id: userId})) .then(...) .catch(...)