掌握 JavaScript Promise 的五个专业技巧
事件处理,尤其是 Promise,绝对是 JavaScript 最棒的特性。你可能已经熟悉这个概念了,简单来说,PromiseJavaScript 中的Promise 就是一个会返回结果的回调函数。
因此,一个 Promise 可以由两个函数构成:一个在成功时调用,另一个在出错时调用。以下是一个会在一秒后随机失败或被拒绝的 Promise:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const randomBool = Math.random() > 0.5;
console.log(randomBool);
// Return any value, or no value at all
if (randomBool) resolve("I am resolved!");
// Reject with an error, some value or nothing at all
else reject("On no!");
}, 1_000);
});
在浏览器控制台或 Node REPL 中尝试运行此命令(node不带任何参数)。一秒钟后,您应该会在控制台中看到true或false看到一条日志。如果 Promise 失败,您会看到一条错误消息(或一条警告,提示 Promise 未在 Node 中捕获)。现在我们有了可以尝试的东西,接下来就是我承诺的技巧(一语双关):
秘诀一:承诺从现在开始
正如你在示例中看到的,即使没有使用 `resolve` 或 `reject` 链式调用,Promise 也会被解析或拒绝.then。.catch一旦await你创建了 Promise,它就会立即开始执行它被告知要执行的操作。
提示二:一旦完成,承诺将反复产生相同的结果
尝试promise.then(console.log)在之前示例中定义 Promise 的同一个控制台或 REPL 中运行。它会一遍又一遍地记录完全相同的结果,没有任何延迟。尝试记录日志console.log(promise),你看到了什么?我猜是以下两种情况之一:
Promise {<rejected>: "On no!"}
或者,如果问题已解决:
Promise { "I am resolved!" }
你可能已经猜到了,Promise 可以处于三种状态之一:已解析pending、已赋值rejected或fulfilled已释放。关键在于,它会一直保持最终状态,直到垃圾回收器将其彻底清除🪦。
技巧三:Promise.prototype.then 接受两个回调函数
then你可以通过链式调用来获得 Promise 结果catch:
promise.then(console.log).catch(console.error)
或者,简单来说:
promise.then(console.log,console.error)
技巧 4:Promise.prototype.then 和 Promise.prototype.catch 都会返回一个新的 Promise。
即使Promise 已被解析console.log(promise.then(()=>{},()=>{})),你仍然会收到错误Promise { <pending> }。但这并不意味着异步操作本身会被重试,而只是这些方法总是会创建一个新的 Promise,即使你的回调函数是同步的。
promise === promise.then(()=>{},()=>{})
// false
promise === promise.then(()=>promise,()=>promise)
// false
技巧五:在适当的时候使用 Promise.all、Promise.race 和 async/await
在 ES5 引入async-await语法之前,我们都生活在回调地狱中:
promise.then(() => {
promise.then(() => {
promise.then(() => {
promise.then(() => {
console.warn("Callback hell in action");
});
});
});
});
但需要记住的是,async/await只是对这种结构的语法糖。其本质仍然是同一个链式调用,这意味着下一个 Promise只有在前一个 Promise 完成之后才会创建:
const createTimeoutPromise = (n, timeout) =>
new Promise((resolve) =>
setTimeout(() => {
console.log(`Promise #${n} is fulfilled`);
resolve(n);
}, timeout)
);
(async () => {
const now = Date.now();
await createTimeoutPromise(1, 1_000);
await createTimeoutPromise(2, 1_000);
await createTimeoutPromise(3, 1_000);
console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();
// Promise #1 is fulfilled
// Promise #2 is fulfilled
// Promise #3 is fulfilled
// Operation took 3.0 s
因此,如果您只想完成所有任务,无论顺序如何,请使用以下方法Promise.all加快速度:
(async () => {
const now = Date.now();
const results = await Promise.all([
createTimeoutPromise(1,1_000),
createTimeoutPromise(2,999),
createTimeoutPromise(3,998),
]);
console.log(results)
console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();
// Promise #3 is fulfilled
// Promise #2 is fulfilled
// Promise #1 is fulfilled
// [ 1, 2, 3 ]
// Operation took 1.0 s
如您所见,尽管承诺的履行顺序并非如此,但您仍将按照您指定的顺序获得承诺的结果。
在极少数情况下,你可能不需要履行所有承诺,只需履行其中任何一个Promise.race即可。为了得到父亲的恩宠,就让他们履行吧👑:
(async () => {
const now = Date.now();
const results = await Promise.race([
createTimeoutPromise(1,1_000),
createTimeoutPromise(2,999),
createTimeoutPromise(3,998),
]);
console.log(results)
console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();
// Promise #3 is fulfilled
// 3
// Operation took 1.0 s
// Promise #2 is fulfilled
// Promise #1 is fulfilled
请记住,如果其中任何一项承诺未能兑现,双方Promise.all都Promise.race将拒绝。
今天就到这里啦,但我保证以后还会有更多(明白我的意思吗?)。
您还有其他妙招吗?欢迎在评论区分享!
照片由Andrew Petrov拍摄,来自Unsplash
文章来源:https://dev.to/valeriavg/ Five-pro-tips-to-master-promises-in-js-c2h