跳到主要内容

异步

异步编程

js 为什么是单线程

  • 作为浏览器脚本语言,其目的是用户交互,操作 DOM;
  • 若使用多线程,会导致用户交互和 DOM 操作的冲突,带来复杂的线程同步问题;
  • 虽然可以使用"锁"的机制避免同步问题,但其大大提高 js 复杂性;

js 为什么设计异步

  • 由于 js 单线程,导致长时间任务会堵塞主线程;
  • 堵塞浏览器 UI 渲染,进而影响用户体验;
  • 因此通过异步的方式避免主线程堵塞问题;

异步任务和同步任务

  • 同步任务:主线程顺序排队执行;
  • 异步任务;
    • 基于任务队列;
    • 在对应异步操作完成后,放入主线程执行;
    • 不会堵塞主线程;

事件循环

事件循环
  • 通过主线程,任务队列,微任务和宏任务,实现异步编程;
宏任务和为任务
  • 宏任务:宿主发起;
    • script 标签;
    • setXXX();
    • postMessage/MessageChannel;
    • IO 操作;
    • UI Render;
  • 微任务:js 发起;
    • promise;
    • MutationObserver;
    • await/async;
    • process.nextTick;
事件循环执行顺序
  • 同步代码 (仅在第一次时间循环);
  • 宏任务;
  • 微任务;
  • requestAnimationRequest;
  • DOM 渲染;
  • requestCallbackIdle;
事件循环流程
  • 执行全局主线程,顺序执行主线程同步代码;
    • 第二次事件循环后无同步代码;
    • 遇到宏任务,推送到宏任务队列,微任务同理;
  • 执行宏任务任务队列,按入队顺序执行第一个宏任务;
  • 执行完该宏任务,移除队列,根据入队顺序执行微任务任务队列,直至微任务任务队列清空;
  • 微任务任务队列清空后,进行浏览器渲染,本次事件循环结束;
  • 执行宏任务任务队列下一个宏任务,开始下一个事件循环,直至宏任务任务队列清空;
  • 执行宏任务或微任务,遇到宏任务,推送到宏任务队列,微任务同理;
    • 当前事件循环添加的微任务当前事件循环清空;
    • 宏任务就顺序添加至宏任务任务队列;
计时线程
  • setXXX 具有单独线程用于计时;
  • 通过不断遍历计时器任务判断其是否到达计时时间;
  • 到达计时时间后将其推送到宏任务队列;

promise

基础

定义 promise
  • promise 为同步对象,但也是连接异步对象的媒介;
  • pending,fulfilled 和 rejected 三种状态,分别为执行中,执行成功,执行失败;
  • resolve() 和 reject() 并不会使 promise 直接进入成功或失败状态,而是执行完函数代码后进入对应状态;
let p1 = new Promise((resolve, reject) => resolve());
let p2 = new Promise((resolve, reject) => reject());
使用 promise
  • promise 只能切换一次状态;
  • 默认状态为 pending;
//
let p = new Promise((resolve, reject) => {
setTimeout(reject, 10000);
// ...
});
p.then(null, () => onRejected(p));
同步异步的二元性
  • 同种模式的代码可以相互执行;
  • 不同种模式的代码无法相互执行;
try {
Promise.reject(new Error("bar"));
} catch (e) {
console.log(e);
}
// Uncaught (in promise) Error: bar
解决值和拒绝理由
  • 解决值或拒绝理由作为 onResolved 和 onRejected 的唯一参数;
let p1 = new Promise((resolve, reject) => resolve("foo"));
p1.then((value) => console.log(value)); // foo
let p2 = new Promise((resolve, reject) => reject("bar"));
p2.catch((reason) => console.log(reason)); // bar
错误处理
  • Promise 的错误处理,只能通过异步的 onRejected 捕获;
  • promise 报错,在其后的同步代码执行后才会抛出错误;
  • catch 正确处理错误后返回一个 fulfilled 状态的 promise;
// 使用 throw 会返回 rejected 状态的 promise
let p = new Promise((resolve, reject) => {
throw Error("foo");
}); // Promise <rejected>: Error: foo

Promise.reject(Error("foo"));
console.log("bar");
// bar
// Uncaught (in promise) Error: foo

new Promise((resolve, reject) => {
console.log("begin asynchronous execution");
reject(Error("bar"));
})
.catch((e) => {
console.log("caught error", e);
})
.then(() => {
console.log("continue asynchronous execution");
});
// begin asynchronous execution
// caught error Error: bar
// continue asynchronous execution
promiseLike
  • 实现 promiseLike 接口的对象等效于 Promise;
  • promiseLike 接口只需实现 then 方法;
const obj = {
then(resolve, reject) {
resolve("Hello, world!");
},
};
const promise = Promise.resolve(obj);
promise.then((value) => {
console.log(value); // 输出 "Hello, world!"
});

常用 API

resolve()
// 两者等效
const p1 = Promise.resolve(123);
const p2 = new Promise((resolve, reject) => {
resolve(123);
});
// 参数若为值, 返回值为 123 的 fulfilled 状态的 promise
const p1 = Promise.resolve(123); //
// 参数若为 promise, 返回 p1 本身
const p2 = Promise.resolve(p1);
reject()
// 两者等效
const p1 = Promise.reject(123);
const p2 = new Promise((resolve, reject) => {
reject(123);
});
// 参数若为值, 返回值为 123 的 rejected 状态的 promise
const p1 = Promise.reject(123); //
// 参数若为 promise, 返回新的与 p1 等效的 promise
const p2 = Promise.reject(p1); // p1 和 p2 不是一个实例
then()
  • 在 p1 进入对应的 resolve 或 reject 状态时,解包 p1 返回的 promise 值作为第一个参数;
  • 立刻返回一个新的与 p1 等效的 pending 状态的 promise 实例;
  • 若不报错返回一个与 p1 等效的的 fulfilled 状态的 promise 的实例,反之返回 rejected 状态的 promise 实例;
  • onFulfilled 省略使用 (x) => x 替换;
  • onRejected 省略 使用 (x) => throw x 替换;
const p1 = new Promise((resolve, reject) => {
// ...
});
p1.then(
(value) => {
// resolve
console.log(value);
},
(reason) => {
// reject
console.error(reason);
}
);

// 返回一个值为 1 的 fulfilled 状态的 promise
p1.then((value) => 1);
// 返回一个值为 undefined 的 fulfilled 状态的 promise
p1.then((value) => {});
// 返回一个值为 123 的 fulfilled 状态的 promise
p1.then((value) => Promise.resolve(123));
// 返回一个值为 123 的 rejected 状态的 promise
p1.then((value) => Promise.reject(123));
catch()
  • Promise.prototype.then(null,onRejected) 的语法糖;
  • 立刻返回一个新的 pending 状态的等价的 Promise 实例;
  • 若不报错最终返回一个新的 fulfilled 状态的等价的 promise,反之返回报错生成的 rejected 状态的 promise;
finally()
  • 总会执行;
  • 立刻返回一个新的 pending 状态的等价的 Promise 实例;
  • 若不报错最终返回一个新的 fulfilled 状态的等价的 promise,反之返回报错生成的 rejected 状态的 promise;
all()
  • 所有 promise 异步计算;
  • 合成 iterable 中的所有 promise,在所有 promise 解决后再返回解决;
  • 若全部解决,返回 iterable 的解决值数组;
    • 数组顺序和 iterable 中 promise 顺序一致;
  • 若存在 promise 拒绝,返回相同状态的 promise;
    • 第一个拒绝的 promise 作为拒绝理由,其余 promise 不处理;
allSettled()
  • 所有 promise 异步计算;
  • 合成 iterable 中的所有 promise,在所有 promise 解决或拒绝后后再返回解决;
    • 该 API 永远不会存在拒绝状态;
  • 返回一个包含各 promise 状态和对应值的数组;
    • 数组顺序和 iterable 中 promise 顺序一致;
any()
  • 合成 iterable 中的所有 promise,在任一 promise 解决后返回解决;
    • 对应 promise 作为解决值;
  • 若全部拒绝,返回 iterable 的拒绝值数组;
race()
  • 合成 iterable 中的所有 promise,在任一 promise 解决或拒绝后再返回;
  • 对应 promise 作为解决值/拒绝值;
all,any,race 是否终止其他 promise
  • 三者返回结果后,并不会终止其他 promise 的执行,但是无法获得其执行结果;

async/await

async

声明 async
let bar = async function () {};
let baz = async () => {};
返回值
  • 立刻返回一个 pending 状态的 Promise;
  • 代码运行成功返回 resolve 状态的 promise;
  • 代码报错返回 rejected 状态的 promise;
async function foo() {
console.log(1);
return 3;
}
foo().then(console.log);
console.log(2);
// 1
// 2
// 3
错误处理
  • 使用 throw 会返回 rejected 状态的 promise;
  • 返回 Promise.reject(3) 进入 catch;
  • 使用 Promise.reject(3) 直接报错;
async function foo() {
console.log(1);
throw 3;
}
foo().catch(console.log);
console.log(2);
// 1
// 2
// 3

async function foo() {
return Promise.reject(3);
}
foo().catch((error) => console.log(error));
// 3

async function foo() {
Promise.reject(3);
}
foo().catch((error) => console.log(error));
// Uncaught (in promise): 3
async 的传染性
  • 底层函数使用了 async;
  • 基于其的调用链必须使用 await;
  • 或者使用 Promise.then() 停止传染;

await

声明 await
async function baz() {
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
console.log("baz");
}
baz(); // baz (1000 毫秒后)
await 机制
  • 执行 await 后的表达式中的同步代码;
  • 暂停异步函数,后面的代码作为整体,推送至微服务消息队列,等待表达式返回值;
  • 跳出异步函数,执行主线程其后的同步代码;
  • 若返回值可用,从微服务队列移除,恢复异步函数代码执行;
let i = 0;

queueMicrotask(function test() {
i++;
console.log("microtask", i);
if (i < 3) {
queueMicrotask(test);
}
});

(async () => {
console.log("async function start");
for (let i = 1; i < 3; i++) {
await null;
console.log("async function resume", i);
}
await null;
console.log("async function end");
})();

queueMicrotask(() => {
console.log("queueMicrotask() after calling async function");
});

console.log("script sync part end");

// async function start
// script sync part end
// microtask 1
// async function resume 1
// queueMicrotask() after calling async function
// microtask 2
// async function resume 2
// microtask 3
// async function end
返回值
  • await 会解包 promise,即 then() 中的 result;
  • 若返回对象不是 promise,视其为已经解决的期约;
  • 拒绝期约的错误不会被异步函数捕获,但对其使用 await 会被异步函数捕获;
async function foo() {
console.log(await "foo");
}
foo();
// foo
async function qux() {
console.log(await Promise.resolve("qux"));
}
qux();
// qux

async function foo() {
console.log(1);
await Promise.reject(3); // 解包 promise
console.log(4); // 不会执行
}
foo().catch((error) => console.log(error));
console.log(2);
// 1
// 2
// 3
错误处理
  • 使用 await 可解包 promise 或 async 中的 Error;
  • 通过 promise 的 then() 和 catch() 返回不同值;
try {
await Promise.reject(new Error("Oops!"));
} catch (error) {
error.message; // "Oops!"
}

export function to<T, U = Error>(
promise: Promise<T>,
errorExt?: object
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data]) // 执行成功, 返回数组第一项为 null, 第二个是结果
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined]; // 执行失败, 返回数组第一项为错误信息, 第二项为 undefined
});
}
await 的限制
  • 只能在 async 函数中使用;
async function foo() {
console.log(await Promise.resolve(3));
}
不使用 await 的 async 函数
  • 若不使用 await,async 和普通函数没有区别;
async function foo() {
console.log(2);
}
console.log(1);
foo();
console.log(3);
// 1
// 2
// 3
顶层 await
  • 模块最外层使用 await 关键字;
  • 只能在 esm 模块且 ES2022 之后版本使用;

promise 和 await/async 的异同

  • 相似性;
    • 两者皆是解决异步操作,避免回调地狱;
    • await/async 是 promise 的语法糖;
    • 两者都支持链式调用;
  • 差异;
    • 语法差异:promise 类似于回调,await/async 类似于同步代码;
    • 错误处理:promise 使用 catch 方法,await/async 使用 try-catch;
    • 可读性:await/async 可读性高;

await/async 实现原理

  • 基于 Promise 和 generator 实现;
    • 将 async/await 内部代码转换为一个状态机;
    • 通过生成器函数返回的迭代器进行状态的管理和切换;
  • async 调用,立刻返回 Promise 对象;
  • 遇到 await,async 暂停执行,将控制权交给迭代器对象,执行 next() 方法等待 promise 状态变化进行下一步操作;
  • 等待过程中,继续执行下一个 await 之前的同步代码;
  • promise 解决,async 再次调用并执行;

最佳实践

promise

连用 Array.map() 和 Promise.all()
  • 假设有多个异步操作;
  • 需等待多个异步操作全部完成后才会执行下一个操作;
  • 使用 Array.map() 创建一个 promise 数组;
  • 使用 await Promise.all() 等待 promise 数组中的 promise 全部完成;
export const loadJsonToConsensusTable = async (filePath: string) => {
const json = JSON.parse((await readFile(filePath)).toString()) as Consensus[];

const promiseList = json.map(async (consensus) => {
// ...
});

await Promise.all(promiseList);
};
promise 超时控制
  • 额外定义一个定时器 Promise 对象;
  • 将目标 Promise 和定时器 Promise 对象构建 promise 数组,基于 race 方法实现超时控制;
const timeoutPromise = (promise, timeout) => {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Promise timed out after " + timeout + " ms"));
}, timeout);
});

return Promise.race([promise, timeoutPromise]);
};
基于 promise 的静态调度器
const createSchedule = (requestList, max) => {
let runningSize = 0;
let finishSize = 0;
const queue = Array.from({ length: requestList.length }, (_, index) => [
requestList[index],
index,
]);
const res = new Array(requestList.length);

return new Promise((resolve, reject) => {
const handle = () => {
if (finishSize === requestList.length) {
resolve(res);
}
if (runningSize >= max || queue.length === 0) return;

const [task, index] = queue.shift();
task()
.then(
(value) => {
res[index] = value;
},
(reason) => {
res[index] = reason;
}
)
.finally(() => {
runningSize--;
finishSize++;
handle();
});
runningSize++;
};

for (let i = 0; i < requestList.length; i++) {
handle();
}
});
};

const start = Date.now();
async function mock() {
await new Promise((resolve) => setTimeout(resolve, 1000));

if (Math.random() > 0.8) {
throw {
status: "error",
value: Date.now() - start,
};
} else {
return {
status: "success",
value: Date.now() - start,
};
}
}

createSchedule([mock, mock, mock, mock, mock], 2).then((res) => {
console.log(res);
});
// [
// { status: 'success', value: 1011 },
// { status: 'error', value: 1011 },
// { status: 'success', value: 2024 },
// { status: 'success', value: 2024 },
// { status: 'error', value: 3037 }
// ]
基于 promise 的动态调度器
const createSchedule = (max) => {
let runningSize = 0;
const queue = [];

return (request) => {
return new Promise((resolve, reject) => {
queue.push({ task: request, resolve, reject });

const handle = () => {
if (queue.length === 0 || runningSize >= max) return;

const { task, resolve, reject } = queue.shift();
task()
.then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason);
}
)
.finally(() => {
runningSize--;
handle();
});
runningSize++;
};

handle();
});
};
};

const start = Date.now();
async function mock1() {
return new Promise((resolve) =>
setTimeout(() => {
resolve(Date.now() - start);
}, 500)
);
}
async function mock2() {
return new Promise((resolve) =>
setTimeout(() => {
resolve(Date.now() - start);
}, 2000)
);
}

async function mock3() {
return new Promise((resolve) =>
setTimeout(() => {
resolve(Date.now() - start);
}, 300)
);
}

async function mock4() {
return new Promise((resolve) =>
setTimeout(() => {
resolve(Date.now() - start);
}, 400)
);
}

const schedule = createSchedule(2);
schedule(mock1).then((value) => {
console.log(value);
});
schedule(mock2).then((value) => {
console.log(value);
});
schedule(mock3).then((value) => {
console.log(value);
});
schedule(mock4).then((value) => {
console.log(value);
});
// 507
// 821
// 1228
// 2008
Promise A+ 规范
  • 关于 Promise 的标准规范,解决不同 promise 实现的兼容性和互操作问题;
    • 定义 promise 状态;
    • 定义 promise 行为;
    • 定义 promise 方法;
手写 promise
  • 创建 Promise 类,接受一个函数;
    • 包括 reject 和 resolve 两个方法;
    • 用于转换 Promise 实例状态;
  • 定义 Promise 属性;
    • state:当前状态;
    • value:返回值;
    • callbackList:缓存 promise 状态转换后需要调用的函数;
  • 定义 resolve 和 reject 方法;
    • 转换 promise 实例 state 和 value;
    • 若存在回调函数,调用回到函数;
  • 定义 then 方法;
    • 接受两个参数 onResolved 和 onRejected;
    • 处理两个参数为空的情况;
    • 返回一个新的 Promise 实例,用于链式调用;
      • 定义 handle 处理 promise 返回值;
      • 根据 promise 实例状态执行不同操作;
  • 使用
const PENDING = "pending"; // 初始未确定的状态
const RESOLVED = "resolved"; // 成功的状态
const REJECTED = "rejected"; // 失败的状态

class MyPromise {
constructor(executor) {
this.state = PENDING;
this.value = null;
this.callbackList = []; // 缓存链式调用的回调函数

const resolve = (value) => {
// 修改 promise 状态
if (this.state !== PENDING) return;
this.state = RESOLVED;
this.value = value;

// 执行缓存的回调函数
if (this.callbackList.length > 0) {
// 添加至微任务队列
queueMicrotask(() => {
this.callbackList.forEach((obj) => {
obj.onResolved();
});
});
}
};

const reject = (value) => {
// 修改 promise 状态
if (this.state !== PENDING) return;
this.state = REJECTED;
this.value = value;

// 执行缓存的回调函数
if (this.callbackList.length > 0) {
// 添加至微任务队列
queueMicrotask(() => {
this.callbackList.forEach((obj) => {
obj.onRejected();
});
});
}
};

try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}

then(onResolved, onRejected) {
// 回调函数默认值
onResolved =
typeof onResolved === "function" ? onResolved : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};

return new MyPromise((resolve, reject) => {
// 处理 promise 返回值
const handle = (callback) => {
try {
const result = callback(this.value);
if (!(result instanceof MyPromise)) {
// 返回值不是 promise, 返回 resolve 状态的 promise
resolve(result);
} else {
// 返回值是promise, 返回该 promise 的对应结果
result.then(
(value) => resolve(value),
(reason) => reject(reason)
);
}
} catch (error) {
// 抛出错误, 返回 reject 状态的 promise
reject(error);
}
};

if (this.state === RESOLVED) {
queueMicrotask(() => {
handle(onResolved);
});
} else if ((this.state = REJECTED)) {
queueMicrotask(() => {
handle(onRejected);
});
} else {
this.callbackList.push({
onResolved: () => {
handle(onResolved);
},
onRejected: () => {
handle(onRejected);
},
});
}
});
}

catch(onRejected) {
return this.then(undefined, onRejected);
}
}

console.log("start");

setTimeout(() => {
console.log("setTimeout");
});

new MyPromise((resolve, reject) => {
console.log("promise1");
resolve("resolve");
})
.then((value) => {
console.log(value);
return "promise then then";
})
.then((value) => {
console.log(value);
});

new MyPromise((resolve, reject) => {
console.log("promise2");
reject("reject");
}).then(null, (value) => {
console.log(value);
});

console.log("end");

// start
// promise1
// promise2
// end
// resolve
// reject
// promise then then
// setTimeout
手写 Promise.all
  • Promise.resolve(promise) 是将 iterable 中的非 promise 转换成 promise;
const customPromiseAll = (iterable) => {
return new Promise((resolve, reject) => {
const promises = Array.from(iterable);
const results = [];
let completedCount = 0;

if (promises.length === 0) {
resolve(results);
return;
}

promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((result) => {
results[index] = result;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
})
.catch((error) => {
reject(error);
});
});
});
};

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
customPromiseAll([promise1, promise2, promise3])
.then((results) => {
console.log(results); // [1, 2, 3]
})
.catch((error) => {
console.error(error);
});
手写 Promise.race()
const customPromiseRace = (iterable) => {
return new Promise((resolve, reject) => {
const promises = Array.from(iterable);

promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((result) => {
resolve(result);
})
.catch((error) => {
reject(error);
});
});
});
};
延迟化和链式调用
class Person {
constructor() {
this.queue = Promise.resolve();
this.origin = Date.now();
}

eat() {
this.queue = this.queue.then(() => {
console.log("eat", Date.now() - this.origin);
});

return this;
}

sleep(time) {
this.queue = this.queue.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
});

return this;
}
}

const p = new Person();
p.eat().sleep(1000).eat().sleep(2000).eat();

async/await

循环中的异步
// array 的 foreach() 方法回调函数中 await 无效;
files.forEach(async (file) => {
const contents = await fs.readFile(file, "utf8");
console.log(contents);
});
// 可使用 for, for of 循环替代;
for (const file of files) {
const contents = await fs.readFile(file, "utf8");
console.log(contents);
}
// map 中 await 有效, 且总是返回 promise 数组
// 建议不要在 filter 和 reduce 中使用 await
实现 sleep()
async function sleep(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
async function foo() {
const t0 = Date.now();
await sleep(1500); // 暂停约 1500 毫秒
console.log(Date.now() - t0);
}
foo();
// 1502
如何判断 async 函数
  • 判断函数构造函数;
  • 判断函数字符串名称;
  • 判断函数返回值;
function isAsyncFunction(fn) {
return fn.constructor.name === "AsyncFunction";
}

function isAsync(fn) {
return Object.prototype.toString.call(async function () {}); // '[object AsyncFunction]'
}

function isAsyncFunction(fn) {
return fn() instanceof Promise;
}
代码执行顺序
Promise.resolve("1")
.then((res) => {
console.log(res);
})
.finally(() => {
console.log("finally");
});
Promise.resolve("2")
.finally(() => {
console.log("finally2");
return "我是finally2返回的值";
})
.then((res) => {
console.log("finally2后面的then函数", res);
});

// 1
// finally2
// finally
// finally2后面的then函数 2

const foo = async () => {
const bar = async () => {
console.log("bar");
};
console.log("bar before");
await bar().then(() => {
console.log("bar then");
});
console.log("bar after");
};
const main = async () => {
console.log("foo before");
foo().then(() => {
console.log("foo then");
});
console.log("foo after");
};
main();
// 执行 main()
// 打印 foo before
// 执行 foo()
// 打印 bar before
// 遇到 await, 暂停 foo(), 执行 bar()
// 执行 bar() 中的同步代码, 打印 bar
// bar() 执行完毕, 立刻返回 resolve 状态的 promise, 退出 bar()
// await 获取到 bar() 的返回值, 向微服务消息队列推送一个恢复 foo() 异步函数执行的任务
// 退出 foo()
// 打印同步代码 foo after
// 同步代码执行完成
// 恢复 foo() 异步函数执行
// 执行 bar().then(), 打印 bar then
// 打印 bar after
// foo() 返回 resolve 状态的 promise, 退出 bar()
// 执行 foo().then(), 打印 foo then

const async1 = async () => {
console.log("async1 start");
await async2();
console.log("async1 end");
};
const async2 = () => {
console.log("async2");
};

console.log("script start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
async1();
new Promise((resolve) => {
console.log("promise1");
resolve(1);
console.log("resolve after");
}).then(() => {
console.log("promise2");
});
console.log("script end");
// 打印 script start
// 执行 setTimeout(), 将其推送至宏任务消息队列
// 执行 async1()
// 打印 async1 start
// 遇到 await, 暂停 async1(), 执行 async2()
// 执行 async2() 中的同步代码, 打印 async2
// async2() 执行完毕, 返回 fulfilled 状态的 promise, 退出 async2()
// await 获取 async2() 的返回值, 向微服务消息队列推送一个恢复 async1() 执行的任务
// 退出 async1()
// 进入 Promise
// 打印 promise1
// 执行 resolve(1), 切换 promise 状态为 resolved
// 打印 resolve after
// 执行 promise.then(), 将其推送至微服务消息队列
// 打印 script end
// 同步代码执行完成
// 恢复 async1() 执行, 打印 async1 end
// 执行 promise.then(), 打印 promise2
// 执行 setTimeout(), 打印 setTimeout

const sleep = async () => {
return new Promise((resolve) => {
console.log("sleep before");
setTimeout(resolve, 1000);
}).then(() => {
console.log("sleep after");
});
};
const fn = async () => {
console.log("fn");
await sleep();
return null;
};
async function foo() {
console.log(2);
console.log(await fn());
console.log(4);
}

const async1 = async () => {
await async2();
console.log("async1 end");
};
const async2 = () => {
console.log("async2");
};

console.log(1);
foo().then(() => console.log("end"));
async1();
console.log(3);
// 打印 1
// 进入 foo()
// 打印 2
// 遇到 await, 暂停 foo() 执行, 执行 fn()
// 执行 fn() 中的同步代码, 打印 in
// 遇到 await, 暂停 fn(), 执行 sleep()
// 执行 sleep() 中的同步代码
// 打印 sleep before
// 执行 setTimeout(), 将其推送至宏任务消息队列
// 打印 sleep after
// fn 中的 await 等待 sleep() 返回的可用值, 退出 fn()
// foo 等待 fn() 返回的可用值, 退出 foo()
// 执行 async1()
// 遇到 await, 暂停 async1(), 执行 async2()
// 执行 async2() 中的同步代码, 打印 async2
// async2() 执行完毕, 返回 fulfilled 状态的 promise, 退出 async2()
// await 获取 async2() 的返回值, 向微任务消息队列推送一个恢复 async1() 执行的任务
// 退出 async1()
// 打印 3
// 1s 后, 恢复 setTimeout() 执行, 返回 fulfilled 状态的 promise
// 执行 sleep() 函数中的 then(), 打印 sleep after, 返回值为 null 的 fulfilled 状态的 promise
// fn() 中的 await 接受到 sleep() 的可用值, 向微任务消息队列返回一个恢复 fn() 执行的任务
// 恢复 fn() 执行
// 打印 null
// 打印 4
// 返回值为 undefined 的 fulfilled 状态的 promise
// 执行 foo().then(), 打印 end