JavaScript回顾-使用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);
async/await语法糖
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
Promise拒绝事件
当 Promise 被拒绝时,会有下文所述的两个事件之一被派发到全局作用域(通常而言,就是window
;如果是在 web worker 中使用的话,就是 Worker
或者其他 worker-based 接口)。这两个事件如下所示:
rejectionhandle
当 Promise 被拒绝、并且在reject
函数处理该 rejection 之后会派发此事件。Unhandledrejection
当 Promise 被拒绝,但没有提供reject
函数来处理该 rejection 时,会派发此事件。
以上两种情况中,PromiseRejectionEvent
事件都有两个属性,一个是 promise
属性,该属性指向被驳回的 Promise,另一个是 reason
属性,该属性用来说明 Promise 被驳回的原因。
一个特别有用的例子:当你使用 Node.js 时,有些依赖模块可能会有未被处理的 rejected promises,这些都会在运行时打印到控制台。你可以在自己的代码中捕捉这些信息,然后添加与 unhandledrejection
相应的处理函数来做分析和处理,或只是为了让你的输出更整洁。举例如下:
window.addEventListener("unhandledrejection", event => {
/* 你可以在这里添加一些代码,以便检查
event.promise 中的 promise 和
event.reason 中的 rejection 原因 */
event.preventDefault();
}, false);
使用Promise封装旧式回调
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
通常,Promise 的构造器接收一个执行函数 (executor),我们可以在这个执行函数里手动地 resolve 和 reject 一个 Promise。既然 setTimeout
并不会真的执行失败,那么我们可以在这种情况下忽略 reject。
组合
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 */ });
可以使用一些聪明的 JavaScript 写法实现时序组合:
[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
.then(result3 => { /* use result3 */ });
通常,我们递归调用一个由异步函数组成的数组时,相当于一个 Promise 链:
Promise.resolve().then(func1).then(func2).then(func3);
我们也可以写成可复用的函数形式,这在函数式编程中极为普遍:
const applyAsync = (acc,val) => acc.then(val);
const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
composeAsync()
函数将会接受任意数量的函数作为其参数,并返回一个新的函数,该函数接受一个通过 composition pipeline 传入的初始值。这对我们来说非常有益,因为任一函数可以是异步或同步的,它们能被保证按顺序执行:
const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);
在 ECMAScript 2017 标准中,时序组合可以通过使用 async/await
而变得更简单:
let result;
for (const f of [func1, func2, func3]) {
result = await f(result);
}
/* use last result (i.e. result3) */
时序
为了避免意外,即使是一个已经变成 resolve 状态的 Promise,传递给 then()
的函数也总是会被异步调用:
Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2
- 本文链接:https://archer-lan.github.io/2023/11/20/JavaScript%E5%9B%9E%E9%A1%BE-%E4%BD%BF%E7%94%A8Promise/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。