出现原因
Promise 太麻烦了
let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));p.then((x) => console.log(x)); // 3// 改造下async function foo() {let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));console.log(await p);}foo();
可以结合生成器 最后一个例子 https://www.yuque.com/yejielin/mypn47/um6a15#kFdYK
=================
async
声明

1、普通 执行流程(同步)


2、有返回值的 执行流程
1、return 不是promise对象

返回值一定是Promise,会被包裹到Promise.resolve( )中,因此可以直接接.then(res),res就是return 的结果(没有就是undefined)
2、return 实现then的对象
return 的是一个对象,并且实现了then方法,这样就会直接执行这个then方法,然后把结果传递到后面的then
3、return Promise对象
如果我们的异步函数的返回值是Promise 对象,那么后面Promise的状态会由这个Promise对象决定,是执行then还是执行catch
3、抛出异常的 执行流程
普通函数,运行到 throw 抛出异常,就会报错并终止整个程序继续运行;
而async 异步函数,运行到 throw 抛出异常,不会报错,不会终止整个程序继续运行,而是会作为Promise的reject来传递,后面可以用(必须要用)catch来捕获异常
=================
await
1、后面跟 成功的Promise


也可以直接写new Promise

2、后面跟普通的值


3、后面跟实现then的对象


4、后面跟失败的Promise
会直接跳出运行,把结果返回给外面(且本异步函数后面不执行了),需要用catch接
也可以通过try catch语句捕获
限制
// 不允许:await 出现在了箭头函数中function foo() {const syncFn = () => {return await Promise.resolve('foo');};console.log(syncFn());}// 不允许:await 出现在了同步函数表达式中function baz() {const syncFn = function() {return await Promise.resolve('baz');};console.log(syncFn());}
停止和恢复执行
JavaScript 运行时在碰到 await 关键字时,会记录在哪里暂停执行。
等到 await 右边的值可用了,JavaScript 运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。
async function foo() {console.log(await Promise.resolve('foo'));}async function bar() {console.log(await 'bar');}async function baz() {console.log('baz');}foo();bar();baz();// baz// bar// foo
async function foo() {console.log(2);console.log(await Promise.resolve(8));console.log(9);}async function bar() {console.log(4);console.log(await 6);console.log(7);}console.log(1);foo();console.log(3);bar();console.log(5);// 1// 2// 3// 4// 5// 6// 7// 8// 9
(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中)await 关键字暂停执行,向消息队列中添加一个期约在落定之后执行的任务;
(5) 期约立即落定,把给 await 提供值的任务添加到消息队列;
(6) foo()退出;
(7) 打印 3;
(8) 调用异步函数 bar();
(9)(在 bar()中)打印 4;
(10)(在 bar()中)await 关键字暂停执行,为立即可用的值 6 向消息队列中添加一个任务;
(11) bar()退出;
(12) 打印 5;
(13) 顶级线程执行完毕;
(14) JavaScript 运行时从消息队列中取出解决 await 期约的处理程序,并将解决的值 8 提供给它;
(15) JavaScript 运行时向消息队列中添加一个恢复执行 foo()函数的任务;
(16) JavaScript 运行时从消息队列中取出恢复执行 bar()的任务及值 6;
(17)(在 bar()中)恢复执行,await 取得值 6;
(18)(在 bar()中)打印 6;
(19)(在 bar()中)打印 7;
(20) bar()返回;
(21) 异步任务完成,JavaScript 从消息队列中取出恢复执行 foo()的任务及值 8;
(22)(在 foo()中)打印 8;
(23)(在 foo()中)打印 9;
(24) foo()返回。
顺序执行、并行执行
// 定义一个延迟执行async function randomDelay(id) {// 延迟 0~1000 毫秒const delay = Math.random() * 1000;return new Promise((resolve) => setTimeout(() => {console.log(`${id} finished`);resolve();}, delay));}// 顺序执行async function foo() {const t0 = Date.now();await randomDelay(0);await randomDelay(1);await randomDelay(2);await randomDelay(3);await randomDelay(4);console.log(`${Date.now() - t0}ms elapsed`);}foo();// 0 finished// 1 finished// 2 finished// 3 finished// 4 finished// 877ms elapsed// 并行执行async function foo() {const t0 = Date.now();const promises = Array(5).fill(null).map((item, index) => randomDelay(index));for (const p of promises) {console.log(`awaited ${await p}`);}/*相当于const p0 = randomDelay(0);const p1 = randomDelay(1);const p2 = randomDelay(2);const p3 = randomDelay(3);const p4 = randomDelay(4);await p0;await p1;await p2;await p3;await p4;*/console.log(`${Date.now() - t0}ms elapsed`);}foo();// 1 finished// 2 finished// 4 finished// 3 finished// 0 finished// awaited 0// awaited 1// awaited 2// awaited 3// awaited 4// 645ms elapsed
async function foo() {const t0 = Date.now();const p0 = randomDelay(0);const p1 = randomDelay(1);const p2 = randomDelay(2);const p3 = randomDelay(3);const p4 = randomDelay(4);await p0;await p1;await p2;await p3;await p4;setTimeout(console.log, 0, `${Date.now() - t0}ms elapsed`);}foo();// 1 finished// 4 finished// 3 finished// 0 finished// 2 finished// 877ms elapsed
=================
性能
期约与异步函数的功能有相当程度的重叠,但它们在内存中的表示则差别很大。
期约Promise:
JavaScript 引擎会在创建期约时尽可能保留完整的调用栈。
在抛出错误时,调用栈可以由运行时的错误处理逻辑获取,因而就会出现在栈追踪信息中。
当然,这意味着栈追踪信息会占用内存,从而带来一些计算和存储成本。
异步函数async/await:
JavaScript 运行时可以简单地在嵌套函数中存储指向包含函数的指针,就跟对待同步函数调用栈一样。
这个指针实际上存储在内存中,可用于在出错时生成栈追踪信息。
这样就不会像之前的例子那样带来额外的消耗,因此在重视性能的应用中是可以优先考虑的。
=================
实例1:读取文件


实例2:Ajax请求
promise then 方法

async await 方法

实例3:实现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
案例4:连锁传递值
async function addTwo(x) {return x + 2;}async function addThree(x) {return x + 3;}async function addFive(x) {return x + 5;}async function addTen(x) {for (const fn of [addTwo, addThree, addFive]) {x = await fn(x);}return x;}addTen(9).then(console.log); // 19

(见下await)
