JavaScript 异步编程
0x00 概述
-
同步(Synchronous)指任务顺序执行,上一个任务未完成前不会执行下一个任务
console.log(1); console.log(2); console.log(3); // 1 2 3
-
异步(Asynchronous)指任务无序并同时执行
setTimeout(() => console.log(1), 300); setTimeout(() => console.log(2), 100); setTimeout(() => console.log(3), 200); // 2 3 1
0x01 回调函数
-
回调(Callback)是异步编程的重要部分
-
通过回调可以实现将异步的操作转换为同步
function task1(callback) { setTimeout(() => { console.log(1); callback(); }, 300); } function task2(callback) { setTimeout(() => { console.log(2); callback(); }, 100); } function task3(callback) { setTimeout(() => { console.log(3); callback(); }, 200); } task1(() => { task2(() => { task3(() => { console.log("end"); }); }); });
-
当上述案例中的异步任务越来越多,回调函数也就越来越长,形成回调地狱
- 引入 Promise 解决
0x02 Promise
(1)概念与使用
-
Promise 是在 ES6 中引入的类,用于更好地编写复杂的异步任务
- 在 ES6 之前的异步请求的处理方案中,通常通过传递回调函数的方式处理请求结果,由于各个请求对回调函数不统一,每次请求时都需要查看相应的源码,造成效率低下,因此需要约定共同的标准,即 Promise 类
-
在通过
new
创建 Promise 对象时,需要传入一个回调函数(称为 executor)const newPromise = new Promise( (resolve, reject) => {} );
这个回调函数会立即执行,并传入另外两个回调函数:
resolve
:执行 Promise 对象的then
方法传入的回调函数reject
:执行 Promise 对象的catch
方法传入的回调函数
举例:异步请求处理(使用计时器模拟网络请求)
function requestData(url) { return new Promise((resolve, reject) => { setTimeout(() => { if (url === "SRIGT") { resolve({ data: ["SRIGT", "https://www.cnblogs.com/SRIGT"], }); } else { reject({ msg: "Invalid URL " + url, }); } }, 1000); }); } requestData("SR1GT").then( (res) => { console.log(res.data); }, (err) => { console.log(err.msg); } );
-
Promise 在使用时,可被划分为三种状态:
- pending:待定中,即执行
executor()
- fulfilled / resolved:已决议,即执行
resolve()
- rejected:已拒绝,即执行
reject()
new Promise((resolve, reject) => { // pending }).then( (res) => { // fulfilled / resolved }, (err) => { // rejected } );
Promise 的状态一经确定则无法更改
- pending:待定中,即执行
-
resolve
方法参数包括:-
普通参数,如数字、字符串、数组、一般对象等
-
Promise 对象,则当前 Promise 对象的状态由传入的 Promise 对象决定
new Promise((resolve, reject) => { resolve( new Promise((resolve, reject) => { reject("Error Message"); }) ); }).then( (res) => { console.log("res:", res); }, (err) => { console.log("err:", err); // err: Error Message } );
-
具有
then
方法的对象,则当前 Promise 对象的状态由传入的对象的then
方法决定new Promise((resolve, reject) => { resolve({ then: (resolve, reject) => { reject("Error Message"); }, }); }).then( (res) => { console.log("res:", res); }, (err) => { console.log("err:", err); // err: Error Message } );
-
(2)对象方法
-
查看 Promise 的所有对象方法
console.log(Object.getOwnPropertyDescriptors(Promise.prototype));
方法 值 可写性 可枚举性 可配置性 constructor [Function: Promise] true false true then [Function: then] true false true catch [Function: catch] true false true finally [Function: finally] true false true [Symbol(Symbol.toStringTag)] 'Promise' false false true
a. then 方法
-
then
方法是 Promise 对上的方法,其在 Promise 的原型为Promise.prototype.then
- 接受两个参数,分别用于到达 fulfilled 或 rejected 状态时使用的回调函数
-
同一个 Promise 对象可以被多次调用
then
方法- 当 Promise 中的
resolve
方法被调用时,所有then
方法传入的回调函数都会执行
const promise = new Promise((resolve, reject) => { resolve("Resolved"); }); promise.then((res) => console.log("res1:", res)); promise.then((res) => console.log("res2:", res)); promise.then((res) => console.log("res3:", res));
- 当 Promise 中的
-
then
方法可以有返回值,是一个 Promise 对象,从而实现 Promise 的链式调用const promise = new Promise((resolve, reject) => { resolve("SRIGT"); }); promise .then((res) => { console.log(res); // SRIGT return "srigt"; }) .then((res) => { console.log(res); // srigt });
此时,
then
方法的return
实际上遵循了 0x01 章关于resolve
参数说明的内容
b. catch 方法
-
当
executor
抛出异常时,会调用错误处理的回调函数,此时,catch
方法用于传入处理错误的回调函数const promise = new Promise((resolve, reject) => { reject("Error Message"); }); promise.catch((err) => console.log(err));
-
catch
方法支持链式调用-
从外向内捕获错误并执行
const promise = new Promise((resolve, reject) => { resolve("Success"); }); promise .then((res) => { console.log(res); // Success return new Promise((resolve, reject) => { reject("Error 2"); }); }) .catch((err) => console.log("err1:", err)) // Error 2 .catch((err) => console.log("err2:", err)); // [此语句未执行]
-
then
方法在catch
方法后也可以执行const promise = new Promise((resolve, reject) => { reject("Error"); }); promise .catch((err) => console.log("err1:", err)) // err1: Error .catch((err) => console.log("err2:", err)) .then((res) => console.log("res:", res)); // res: undefined
-
-
catch
方法支持多次调用 -
catch
方法可以return
,返回的值是一个 Promise 对象const promise = new Promise((resolve, reject) => { reject("Error"); }); promise .then((res) => console.log("res1", res)) .catch((err) => { console.log("err", err); // err: Error return "Success"; }) .then((res) => console.log("res2", res)); // res2: Success
c. finally 方法
-
finally
方法是 ES9 新加入的特性,无论 fulfilled 还是 rejected 状态都会执行的方法const promise1 = new Promise((resolve, reject) => { resolve("Success"); }); const promise2 = new Promise((resolve, reject) => { reject("Error"); }); promise1.finally(() => console.log("Finally1")); promise2.finally(() => console.log("Finally2"));
Promises/A+ 规范:https://promisesaplus.com/
(3)Promise 类方法
a. resolve 与 reject
-
resolve
const promise = Promise.resolve("Success"); // 相当于 const promise = new Promise((resolve, reject) => { resolve("Success"); })
-
reject
使用与resolve
类似
b. all 与 allSettled
-
all
方法将多个 Promise 包裹为一个新的 Promise-
等待队列(数组参数)中所有 Promise 均处于 fulfilled 状态时执行
then
方法const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success2"); }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success3"); }, 3000); }); Promise.all([promise1, promise2, promise3, "All Over"]).then((res) => console.log(res) // [等待 3 秒后输出] [ 'Success1', 'Success2', 'Success3', 'All Over' ] );
-
当队列中某个 Promise 处于 rejected 状态时,会中断执行并直接进入
catch
方法const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error"); }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success3"); }, 3000); }); Promise.all([promise1, promise2, promise3, "All Over"]) .then((res) => console.log(res)) .catch((err) => console.log(err)); // [等待 2 秒后输出] Error
-
-
allSettled
方法在 ES11 中加入,不会因为某个 Promise 为 rejected 状态而中断,且最终状态一定为 fulfilledconst promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error"); }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success3"); }, 3000); }); Promise.allSettled([promise1, promise2, promise3, "All Over"]) .then((res) => console.log(res)); /* * [ * { status: 'fulfilled', value: 'Success1' }, * { status: 'rejected', reason: 'Error' }, * { status: 'fulfilled', value: 'Success3' }, * { status: 'fulfilled', value: 'All Over' } * ] */
c. race 与 any
-
race
方法特点是:只要有一个 Promise 不是 pending 状态,则中断执行const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error2"); }, 2000); }); Promise.race([promise1, promise2]) .then((res) => console.log(res)) .catch((err) => console.log(err)); // [等待 1 秒后执行] Error
-
any
方法在 ES12 中加入,只要有一个 Promise 是 fulfilled 状态,则中断执行const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error2"); }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success"); }, 3000); }); Promise.any([promise1, promise2, promise3]) .then((res) => console.log(res)) // [等待 3 秒后执行] Success .catch((err) => console.log(err));
-
如果所有 Promise 都是 rejected 状态,则等到所有 Promise 执行结束
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error1"); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error2"); }, 2000); }); const promise3 = new Promise((resolve, reject) => { setTimeout(() => { reject("Error3"); }, 3000); }); Promise.any([promise1, promise2, promise3]) .then((res) => console.log(res)) .catch((err) => console.log(err.errors)); // [等待 3 秒后执行] [ 'Error1', 'Error2', 'Error3' ]
-
(4)MyPromise
-
初步构建 MyPromise,通过
status
保存状态、result
保存结果、_this
保存 MyPromise,配置原型方法then
并测试function MyPromise(executor) { this.status = "pending"; this.result = undefined; const _this = this; function resolve(res) { if (_this.status !== "pending") return; _this.status = "fulfilled"; _this.result = res; } function reject(res) { if (_this.status !== "pending") return; _this.status = "rejected"; _this.result = res; } executor(resolve, reject); } MyPromise.prototype.then = function (successCallback, failureCallback) { if (!successCallback) successCallback = value => value; if (!failureCallback) failureCallback = error => error; if (this.status === "fulfilled") { successCallback(this.result); } else if (this.status === "rejected") { failureCallback(this.result); } };
测试
const promise = new MyPromise((resolve, reject) => { resolve("Success"); reject("Error"); }).then( (res) => console.log(res), (err) => console.log(err) );
-
当在 pending 状态下执行时间较长,则需要收集回调,等待完成后再执行回调;为防止多次调用 Promise 的
then
方法造成后者覆盖前者,采用数组保存回调的收集function MyPromise(executor) { this.status = "pending"; this.result = undefined; this.stack = []; const _this = this; function resolve(res) { if (_this.status !== "pending") return; _this.status = "fulfilled"; _this.result = res; _this.stack.forEach((item) => item.successCallback(res)); } function reject(res) { if (_this.status !== "pending") return; _this.status = "rejected"; _this.result = res; _this.stack.forEach((item) => item.failureCallback(res)); } executor(resolve, reject); } MyPromise.prototype.then = function (successCallback, failureCallback) { if (!successCallback) successCallback = value => value; if (!failureCallback) failureCallback = error => error; if (this.status === "fulfilled") { successCallback(this.result); } else if (this.status === "rejected") { failureCallback(this.result); } else { this.stack.push({ successCallback, failureCallback, }); } };
测试
const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("Success2"); }, 1000); }).then( (res) => console.log(res), (err) => console.log(err) ); const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("Success2"); }, 1000); }).then( (res) => console.log(res), (err) => console.log(err) );
-
实现链式调用
function MyPromise(executor) { this.status = "pending"; this.result = undefined; this.stack = []; const _this = this; function resolve(res) { if (_this.status !== "pending") return; _this.status = "fulfilled"; _this.result = res; _this.stack.forEach((item) => item.successCallback(res)); } function reject(res) { if (_this.status !== "pending") return; _this.status = "rejected"; _this.result = res; _this.stack.forEach((item) => item.failureCallback(res)); } executor(resolve, reject); } MyPromise.prototype.then = function (successCallback, failureCallback) { if (!successCallback) successCallback = value => value; if (!failureCallback) failureCallback = error => error; return new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { const result = successCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { resolve(result); } } else if (this.status === "rejected") { const result = failureCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { reject(result); } } else { this.stack.push({ successCallback: () => { const result = successCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { resolve(result); } }, failureCallback: () => { const result = failureCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { reject(result); } }, }); } }); };
测试
const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("Success1"); }, 1000); }) .then( (res) => { console.log("res1:", res); return new MyPromise((resolve, reject) => { setTimeout(() => { reject("Error2"); }, 1000); }); }, (err) => console.log("err1:", err) ) .then( (res) => console.log("res2:", res), (err) => console.log("err2:", err) );
-
实现
catch
方法MyPromise.prototype.catch = function (failureCallback) { this.then(undefined, failureCallback); };
-
完整自制的 MyPromise
function MyPromise(executor) { this.status = "pending"; this.result = undefined; this.stack = []; const _this = this; function resolve(res) { if (_this.status !== "pending") return; _this.status = "fulfilled"; _this.result = res; _this.stack.forEach((item) => item.successCallback(res)); } function reject(res) { if (_this.status !== "pending") return; _this.status = "rejected"; _this.result = res; _this.stack.forEach((item) => item.failureCallback(res)); } executor(resolve, reject); } MyPromise.prototype.then = function (successCallback, failureCallback) { if (!successCallback) successCallback = () => {}; if (!failureCallback) failureCallback = () => {}; return new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { const result = successCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { resolve(result); } } else if (this.status === "rejected") { const result = failureCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { reject(result); } } else { this.stack.push({ successCallback: () => { const result = successCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { resolve(result); } }, failureCallback: () => { const result = failureCallback(this.result); if (result instanceof MyPromise) { result.then( (res) => resolve(res), (err) => reject(err) ); } else { reject(result); } }, }); } }); }; MyPromise.prototype.catch = function (failureCallback) { this.then(undefined, failureCallback); };
0x03 async 与 await
-
async
与await
在 ES7 中加入,使异步代码看起来和写起来更像是同步代码 -
async
返回 Promise 对象,而await
接收 Promise 对象并返回该对象的结果(如果不是 Promise 对象则返回对应的值)async function requestData() { const result = await new Promise((resolve, reject) => { resolve("Success"); }); return result; } const result = requestData(); console.log(result); result.then( (res) => console.log(res), (err) => console.log(err) );
-End-