JavaScript 异步处理方法梳理,内容涵盖回调函数的基本用法与错误处理、Promise 的创建与链式调用、常用 API 如 all、race、allSettled、any 等使用方式,以及 async/await 的基本语法与典型示例。
回调
1 | function loadScript(src, callback) { |
这被称为“基于回调”的异步编程风格。异步执行某项功能的函数应该提供一个
callback
参数用于在相应事件完成时调用。(译注:上面这个例子中的相应事件是指脚本加载)
处理 Error
在上述示例中,我们并没有考虑出现 error 的情况。如果脚本加载失败怎么办?我们的回调应该能够对此作出反应。
1 | function loadScript(src, callback) { |
加载成功时,它会调用 callback(null, script)
,否则调用
callback(error)
。
Promise
Promise 对象的构造器(constructor)语法如下:
1 | let promise = new Promise(function(resolve, reject) { |
传递给 new Promise
的函数被称为
executor。当 new Promise
被创建,executor
会自动运行。它包含最终应产出结果的生产者代码。
它的参数 resolve
和 reject
是由 JavaScript
自身提供的回调。我们的代码仅在 executor 的内部。
当 executor 获得了结果,无论是早还是晚都没关系,它应该调用以下回调之一:
resolve(value)
—— 如果任务成功完成并带有结果value
。reject(error)
—— 如果出现了 error,error
即为 error 对象。
由 new Promise
构造器返回的 promise
对象具有以下内部属性:
state
—— 最初是"pending"
,然后在resolve
被调用时变为"fulfilled"
,或者在reject
被调用时变为"rejected"
。result
—— 最初是undefined
,然后在resolve(value)
被调用时变为value
,或者在reject(error)
被调用时变为error
。
1 | let promise = new Promise(function(resolve, reject) { |
只能有一个结果或一个error:第一次调用resolve或reject后,函数就执行结束了。
state
和result
都是内部的:无法直接访问。
then
1 | promise.then( |
.then
的第一个参数是一个函数,该函数将在 promise
resolved 且接收到结果后执行。
.then
的第二个参数也是一个函数,该函数将在 promise
rejected 且接收到 error 信息后执行。
1 | let promise = new Promise(function(resolve, reject) { |
then中第一个函数必须是对successful处理,第二个是对error处理,不能调换。
catch
如果我们只对 error 感兴趣,那么我们可以使用 null
作为第一个参数:.then(null, errorHandlingFunction)
。或者我们也可以使用
.catch(errorHandlingFunction)
,其实是一样的:
1 | let promise = new Promise((resolve, reject) => { |
finally
1 | new Promise((resolve, reject) => { |
finally不关心Promise结果
Promise 链
1 | new Promise(function(resolve, reject) { |
它的想法是通过 .then
处理程序(handler)链进行传递
result。
下面的不是链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});我们在这里所做的只是一个 promise 的几个处理程序。它们不会相互传递 result;相反,它们之间彼此独立运行处理任务。
一个完整的例子
1 | function loadJson(url) { |
promise 进行错误处理
promise 链在错误(error)处理中十分强大。当一个 promise 被 reject 时,控制权将移交至最近的 rejection 处理程序。
1 | fetch('/article/promise-chaining/user.json') |
通常情况下,这样的 .catch
根本不会被触发。但是如果上述任意一个 promise
rejected(网络问题或者无效的 json 或其他),.catch
就会捕获它。
未处理的 rejection
在浏览器中,我们可以使用 unhandledrejection
事件来捕获这类 error:
1 | window.addEventListener('unhandledrejection', function(event) { |
Promise API 示例
Promise.all
1 | let names = ['iliakan', 'remy', 'jeresig']; |
如果任意一个 promise 被 reject,由
Promise.all
返回的 promise 就会立即 reject,并且带有的就是这个 error。
Promise
类有 6 种静态方法:
Promise.all(promises)
—— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成Promise.all
的 error,所有其他 promise 的结果都会被忽略。Promise.allSettled(promises)
(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:status
:"fulfilled"
或"rejected"
value
(如果 fulfilled)或reason
(如果 rejected)。
Promise.race(promises)
—— 等待第一个 settle 的 promise,并将其 result/error 作为结果返回。Promise.any(promises)
(ES2021 新增方法)—— 等待第一个 fulfilled 的 promise,并将其结果作为结果返回。如果所有 promise 都 rejected,Promise.any
则会抛出AggregateError
错误类型的 error 实例。Promise.resolve(value)
—— 使用给定 value 创建一个 resolved 的 promise。Promise.reject(error)
—— 使用给定 error 创建一个 rejected 的 promise。
async/await
async function
该函数总是返回一个 promise。其他值将自动被包装在一个 resolved 的 promise 中
1 | async function f() { |
await
等待特定的promise完成
1 | async function f() { |
非 async 函数中调用 async 函数
如果我们尝试在非 async 函数中使用
await
,则会报语法错误:
1 | function f() { |
只需要把 async
调用当作 promise 对待,并在它的后面加上
.then
即可:
1 | async function wait() { |