Promise对象表示一个异步操作的最终完成(或失败)及其结果值。
对很多人来说,这是一个很难的主题,尤其是当你熟悉的编程语言完全是_同步_的时候。 如果你觉得吃不消,或者想进一步了解并发和并行,可以看看这个精彩的演讲“Concurrency is not parallelism”:在 go.dev 上观看,或者直接在 vimeo 上观看,并阅读幻灯片。
一个Promise有三种状态:
Promise 刚创建时处于待定状态。 在将来的某个时刻,它可能会_兑现_或_拒绝_。 一旦一个 Promise 兑现或拒绝过一次,它就再也不能兑现或拒绝,状态也不会再改变。
换句话说:
Promise 可以通过多种方式兑现:
// Creates a promise that is immediately resolved
Promise.resolve(value);
// Creates a promise that is immediately resolved
new Promise((resolve) => {
resolve(value);
});
// Chaining a promise leads to a resolved promise
somePromise.then(() => {
// ...
return value;
});
在上面的示例中,value可以是_任何东西_,包括错误、undefined、null或另一个 Promise。
通常你会希望用一个不是错误的值来兑现。
Promise 可以通过多种方式拒绝:
// Creates a promise that is immediately rejected
Promise.reject(reason)
// Creates a promise that is immediately rejected
new Promise((_, reject) {
reject(reason)
})
// Chaining a promise with an error leads to a rejected promise
somePromise.then(() => {
// ...
throw reason
})
在上面的示例中,reason可以是_任何东西_,包括错误、undefined或null。
通常你会希望用一个错误来拒绝。
当 Promise 兑现或拒绝后,可以用后续操作让它_继续_下去。
promise兑现后,会调用promise.then()
promise拒绝后,会调用promise.catch()
promise兑现或拒绝后(无论哪种情况),会调用promise.finally()
每个 Promise 都是“thenable”的。
也就是说,有一个名为then的函数,当原始 Promise 兑现后它就会执行。
对于promise.then(onResolved),回调onResolved会收到原始 Promise 兑现时所用的值。
它总是会返回一个_新的_“链式”Promise。
从then返回一个value会让这个“链式”Promise 兑现。
在then中抛出reason会让这个“链式”Promise 拒绝。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
});
const promise2 = promise1.then(function (value) {
console.log(value);
// expected output: "Success!"
return true;
});
这会在大约 1000 毫秒后输出"Success!"。
promise1的状态和值将是resolved和"Success!"。
promise2的状态和值将是resolved和true。
then还可以接收第二个参数,当原始 Promise 拒绝时执行。
对于promise.then(onResolved, onRejected),回调onResolved会收到原始 Promise 兑现时所用的值,或者回调onRejected会收到 Promise 被拒绝的原因。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return true;
}
function shout(reason) {
console.error(reason.toUpperCase());
return false;
}
const promise2 = promise1.then(log, shout);
"Success!"。
promise1的状态和值将是resolved和"Success!"。promise2的状态和值将是resolved和true。"NOPE!"。
promise1的状态和值将是rejected和Nope!。promise2的状态和值将是resolved和false。有一点很重要:根据生命周期的规则,当它reject之后,约 1000 毫秒后才到来的resolve会被静默忽略,因为内部状态一旦拒绝或兑现就无法再改变。
同样重要的是:从 Promise 中返回一个值会让它兑现,抛出一个值会让它拒绝。
当promise1兑现且存在链式的onResolved时,也就是then(onResolved),那么后续得到的还是一个 Promise,它可以兑现,也可以拒绝。
当promise1拒绝但存在链式的onRejected时,也就是then(, onRejected),那么后续得到的还是一个 Promise,它可以兑现,也可以拒绝。
有时你想捕获错误,并且只在原始 Promise reject时才继续。
对于promise.catch(onCatch),回调onCatch会收到原始 Promise 被拒绝的原因。
它总是会返回一个_新的_“链式”Promise。
从catch返回一个value会让这个“链式”Promise 兑现。
在catch中抛出reason会让这个“链式”Promise 拒绝。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return 'done';
}
function recover(reason) {
console.error(reason.toUpperCase());
return 42;
}
const promise2 = promise1.catch(recover).then(log);
大约有一半的情况,这会在约 1000 毫秒后输出"Success!"。
另外一半的情况,这会立即输出42。
promise1兑现,catch会被跳过,流程到达then,并输出该值。
promise1的状态和值将是resolved和"Success!"。promise2的状态和值将是resolved和"done";promise1拒绝,catch会执行,它_返回一个值_,于是这条链现在处于resolved状态,流程到达then,并输出该值。
promise1的状态和值将是rejected和"Nope!"。promise2的状态和值将是resolved和"done";有时你想在 Promise 落定之后执行代码,无论它是兑现还是拒绝。
对于promise.finally(onSettled),回调onSettled不会收到任何内容。
它总是会返回一个_新的_“链式”Promise。
从finally返回一个value会复制原始 Promise 的状态和值,并忽略这个value。
在finally中抛出reason会让这个“链式”Promise 拒绝,并覆盖原始 Promise 的状态和值或原因。
把多个方法组合在一起:
const myPromise = new Promise(function (resolve, reject) {
const sampleData = [2, 4, 6, 8];
const randomNumber = Math.round(Math.random() * 5);
if (sampleData[randomNumber]) {
resolve(sampleData[randomNumber]);
} else {
reject('Sampling did not result in a sample');
}
});
const finalPromise = myPromise
.then(function (sampled) {
// If the random number was 0, 1, 2, or 3, this will be
// reached and the number 2, 4, 6, or 8 will be logged.
console.log(`Sampled data: ${sampled}`);
return 'yay';
})
.catch(function (reason) {
// If the random number was 4 or 5, this will be reached and
// reason will be "An error occurred". The entire chain will
// then reject with an Error with the reason as message.
throw new Error(reason);
})
.finally(function () {
// This will always log after either the sampled data is
// logged or the error is raised.
console.log('Promise completed');
});
randomNumber为0-3时:
myPromise将以值2, 4, 6, or 8兑现finalPromise将以值'yay'兑现Sampled data: ...Promise completedrandomNumber为4-5时:
myPromise将以原因'Sampling did not result in a sample'拒绝finalPromise将以原因Error('Sampling did not result in a sample')拒绝Promise completed"uncaught rejected promise: Error('Sampling did not result in a sample')"日志如上所示,reject可以接受字符串,Promise 也可以用Error来拒绝。
如果对 Promise 的链式调用或一般用法还不太清楚,MDN 上的教程是一个不错的参考资料。