ECMAScript 6 入门教程—async 函数

共 14033字,需浏览 29分钟

 ·

2020-10-23 15:04

作者 | 阮一峰

1、含义

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
前文有一个 Generator 函数,依次读取两个文件。
const fs = require('fs');

const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};

const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
上面代码的函数gen可以写成async函数,就是下面这样。
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数对 Generator 函数的改进,体现在以下四点。
(1)内置执行器。
Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
asyncReadFile();
上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
(2)更好的语义。
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。
co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
(4)返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

2、基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
下面是一个例子。
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
console.log(result);
});
上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
下面是另一个例子,指定多少毫秒后输出一个值。
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}

asyncPrint('hello world', 50);
上面代码指定 50 毫秒以后,输出hello world。
由于async函数返回的是 Promise 对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。
async function timeout(ms) {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}

asyncPrint('hello world', 50);
async 函数有多种使用形式。
// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}

async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}

const storage = new Storage();
storage.getAvatar('jake').then();

// 箭头函数
const foo = async () => {};

3、语法

async函数的语法规则总体上比较简单,难点是错误处理机制。

返回 Promise 对象

async函数返回一个 Promise 对象。
async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() {
return 'hello world';
}

f().then(v => console.log(v))
// "hello world"
上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到。
async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f() {
throw new Error('出错了');
}

f().then(
v => console.log('resolve', v),
e => console.log('reject', e)
)
//reject Error: 出错了

Promise 对象的状态变化

async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
下面是一个例子。
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/([\s\S]+)<\/title>/i</span><span style="color: rgb(248, 248, 242);">)[</span><span style="color: rgb(174, 129, 255);">1</span><span style="color: rgb(248, 248, 242);">];</span><br><span style="color: rgb(248, 248, 242);">}</span><br>getTitle<span style="color: rgb(248, 248, 242);">(</span>'https://tc39.github.io/ecma262/'<span style="color: rgb(248, 248, 242);">).</span>then<span style="color: rgb(248, 248, 242);">(</span>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">)</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// "ECMAScript 2017 Language Specification"<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,函数getTitle内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log。</span></section><h3 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong>await 命令</strong></h3><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 等同于<br></span> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // return 123;<br></span> <span style="color: rgb(102, 217, 239);">return</span> await <span style="color: rgb(174, 129, 255);">123</span><span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>f<span style="color: rgb(248, 248, 242);">().</span>then<span style="color: rgb(248, 248, 242);">(</span>v <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">))</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 123<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,await命令的参数是数值123,这时等同于return 123。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">另一种情况是,await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">class Sleep <span style="color: rgb(248, 248, 242);">{</span><br> constructor<span style="color: rgb(248, 248, 242);">(</span>timeout<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">this</span><span style="color: rgb(248, 248, 242);">.</span>timeout <span style="color: rgb(249, 38, 114);">=</span> timeout<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> then<span style="color: rgb(248, 248, 242);">(</span>resolve<span style="color: rgb(248, 248, 242);">,</span> reject<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> const startTime <span style="color: rgb(249, 38, 114);">=</span> Date<span style="color: rgb(248, 248, 242);">.</span>now<span style="color: rgb(248, 248, 242);">();</span><br> setTimeout<span style="color: rgb(248, 248, 242);">(</span><br> <span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(249, 38, 114);">=></span> resolve<span style="color: rgb(248, 248, 242);">(</span>Date<span style="color: rgb(248, 248, 242);">.</span>now<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(249, 38, 114);">-</span> startTime<span style="color: rgb(248, 248, 242);">),</span><br> <span style="color: rgb(102, 217, 239);">this</span><span style="color: rgb(248, 248, 242);">.</span>timeout<br> <span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br><span style="color: rgb(248, 248, 242);">(</span>async <span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> const sleepTime <span style="color: rgb(249, 38, 114);">=</span> await <span style="color: rgb(102, 217, 239);">new</span> Sleep<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">);</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>sleepTime<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">})();</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 1000<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是 Promise 对象,但是因为定义了then方法,await会将其视为Promise处理。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">这个例子还演示了如何实现休眠效果。JavaScript 一直没有休眠的语法,但是借助await命令就可以让程序停顿指定的时间。下面给出了一个简化的sleep实现。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> sleep<span style="color: rgb(248, 248, 242);">(</span>interval<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> <span style="color: rgb(102, 217, 239);">new</span> Promise<span style="color: rgb(248, 248, 242);">(</span>resolve <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> setTimeout<span style="color: rgb(248, 248, 242);">(</span>resolve<span style="color: rgb(248, 248, 242);">,</span> interval<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">})</span><br><span style="color: rgb(248, 248, 242);">}</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 用法<br></span>async <span style="color: rgb(102, 217, 239);">function</span> one2FiveInAsync<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">for</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> i <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(174, 129, 255);">1</span><span style="color: rgb(248, 248, 242);">;</span> i <span style="color: rgb(249, 38, 114);"><=</span> <span style="color: rgb(174, 129, 255);">5</span><span style="color: rgb(248, 248, 242);">;</span> i<span style="color: rgb(249, 38, 114);">++</span><span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>i<span style="color: rgb(248, 248, 242);">);</span><br> await sleep<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>one2FiveInAsync<span style="color: rgb(248, 248, 242);">();</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> await Promise<span style="color: rgb(248, 248, 242);">.</span>reject<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>f<span style="color: rgb(248, 248, 242);">()</span><br><span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span>v <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">))</span><br><span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">))</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 出错了<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> await Promise<span style="color: rgb(248, 248, 242);">.</span>reject<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">);</span><br> await Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">(</span>'hello world'<span style="color: rgb(248, 248, 242);">);</span><span spellcheck="true" style="color: rgb(117, 113, 94);"> // 不会执行<br></span><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,第二个await语句是不会执行的,因为第一个await语句状态变成了reject。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> await Promise<span style="color: rgb(248, 248, 242);">.</span>reject<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">return</span> await Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">(</span>'hello world'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>f<span style="color: rgb(248, 248, 242);">()</span><br><span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span>v <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">))</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// hello world<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> await Promise<span style="color: rgb(248, 248, 242);">.</span>reject<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">)</span><br> <span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">));</span><br> <span style="color: rgb(102, 217, 239);">return</span> await Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">(</span>'hello world'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>f<span style="color: rgb(248, 248, 242);">()</span><br><span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span>v <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">))</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 出错了<br>// hello world<br></span></code></section></pre><h3 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong>错误处理</strong></h3><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> await <span style="color: rgb(102, 217, 239);">new</span> Promise<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span> <span style="color: rgb(248, 248, 242);">(</span>resolve<span style="color: rgb(248, 248, 242);">,</span> reject<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">throw</span> <span style="color: rgb(102, 217, 239);">new</span> Error<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br><br>f<span style="color: rgb(248, 248, 242);">()</span><br><span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span>v <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">))</span><br><span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">))</span><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// Error:出错了<br></span></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,async函数f执行后,await后面的 Promise 对象会抛出一个错误对象,导致catch方法的回调函数被调用,它的参数就是抛出的错误对象。具体的执行机制,可以参考后文的“async 函数的实现原理”。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">防止出错的方法,也是将其放在try...catch代码块之中。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> f<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> await <span style="color: rgb(102, 217, 239);">new</span> Promise<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span> <span style="color: rgb(248, 248, 242);">(</span>resolve<span style="color: rgb(248, 248, 242);">,</span> reject<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">throw</span> <span style="color: rgb(102, 217, 239);">new</span> Error<span style="color: rgb(248, 248, 242);">(</span>'出错了'<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">return</span> await<span style="color: rgb(248, 248, 242);">(</span>'hello world'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">如果有多个await命令,可以统一放在try...catch结构中。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> main<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> const val1 <span style="color: rgb(249, 38, 114);">=</span> await firstStep<span style="color: rgb(248, 248, 242);">();</span><br> const val2 <span style="color: rgb(249, 38, 114);">=</span> await secondStep<span style="color: rgb(248, 248, 242);">(</span>val1<span style="color: rgb(248, 248, 242);">);</span><br> const val3 <span style="color: rgb(249, 38, 114);">=</span> await thirdStep<span style="color: rgb(248, 248, 242);">(</span>val1<span style="color: rgb(248, 248, 242);">,</span> val2<span style="color: rgb(248, 248, 242);">);</span><br><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>'Final: '<span style="color: rgb(248, 248, 242);">,</span> val3<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">catch</span> <span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>error<span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">下面的例子使用try...catch结构,实现多次重复尝试。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">const superagent <span style="color: rgb(249, 38, 114);">=</span> require<span style="color: rgb(248, 248, 242);">(</span>'superagent'<span style="color: rgb(248, 248, 242);">);</span><br>const NUM_RETRIES <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(174, 129, 255);">3</span><span style="color: rgb(248, 248, 242);">;</span><br><br>async <span style="color: rgb(102, 217, 239);">function</span> test<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> i<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(102, 217, 239);">for</span> <span style="color: rgb(248, 248, 242);">(</span>i <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(174, 129, 255);">0</span><span style="color: rgb(248, 248, 242);">;</span> i <span style="color: rgb(249, 38, 114);"><</span> NUM_RETRIES<span style="color: rgb(248, 248, 242);">;</span> <span style="color: rgb(249, 38, 114);">++</span>i<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> await superagent<span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">get</span><span style="color: rgb(248, 248, 242);">(</span>'http://google.com/this-throws-an-error'<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(102, 217, 239);">break</span><span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{}</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>i<span style="color: rgb(248, 248, 242);">);</span><span spellcheck="true" style="color: rgb(117, 113, 94);"> // 3<br></span><span style="color: rgb(248, 248, 242);">}</span><br><br>test<span style="color: rgb(248, 248, 242);">();</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,如果await操作成功,就会使用break语句退出循环;如果失败,会被catch语句捕捉,然后进入下一轮循环。</span></section><h3 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong>使用注意点</strong></h3><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> myFunction<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> await somethingThatReturnsAPromise<span style="color: rgb(248, 248, 242);">();</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span> <span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 另一种写法<br></span><br>async <span style="color: rgb(102, 217, 239);">function</span> myFunction<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> await somethingThatReturnsAPromise<span style="color: rgb(248, 248, 242);">()</span><br> <span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span> <span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>err<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">第二点,多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">let</span> foo <span style="color: rgb(249, 38, 114);">=</span> await getFoo<span style="color: rgb(248, 248, 242);">();</span><br><span style="color: rgb(102, 217, 239);">let</span> bar <span style="color: rgb(249, 38, 114);">=</span> await getBar<span style="color: rgb(248, 248, 242);">();</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// 写法一<br></span><span style="color: rgb(102, 217, 239);">let</span> <span style="color: rgb(248, 248, 242);">[</span>foo<span style="color: rgb(248, 248, 242);">,</span> bar<span style="color: rgb(248, 248, 242);">]</span> <span style="color: rgb(249, 38, 114);">=</span> await Promise<span style="color: rgb(248, 248, 242);">.</span>all<span style="color: rgb(248, 248, 242);">([</span>getFoo<span style="color: rgb(248, 248, 242);">(),</span> getBar<span style="color: rgb(248, 248, 242);">()]);</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 写法二<br></span><span style="color: rgb(102, 217, 239);">let</span> fooPromise <span style="color: rgb(249, 38, 114);">=</span> getFoo<span style="color: rgb(248, 248, 242);">();</span><br><span style="color: rgb(102, 217, 239);">let</span> barPromise <span style="color: rgb(249, 38, 114);">=</span> getBar<span style="color: rgb(248, 248, 242);">();</span><br><span style="color: rgb(102, 217, 239);">let</span> foo <span style="color: rgb(249, 38, 114);">=</span> await fooPromise<span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(102, 217, 239);">let</span> bar <span style="color: rgb(249, 38, 114);">=</span> await barPromise<span style="color: rgb(248, 248, 242);">;</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面两种写法,getFoo和getBar都是同时触发,这样就会缩短程序的执行时间。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">第三点,await命令只能用在async函数之中,如果用在普通函数,就会报错。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 报错<br></span> docs<span style="color: rgb(248, 248, 242);">.</span>forEach<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span> <span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> await db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码会报错,因为await用在普通函数之中了。但是,如果将forEach方法的参数改成async函数,也有问题。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><span spellcheck="true" style="color: rgb(117, 113, 94);"> //这里不需要 async<br></span> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 可能得到错误结果<br></span> docs<span style="color: rgb(248, 248, 242);">.</span>forEach<span style="color: rgb(248, 248, 242);">(</span>async <span style="color: rgb(102, 217, 239);">function</span> <span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> await db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码可能不会正常工作,原因是这时三个db.post操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用for循环。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br><br> <span style="color: rgb(102, 217, 239);">for</span> <span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> doc of docs<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> await db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">另一种方法是使用数组的reduce方法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br><br> await docs<span style="color: rgb(248, 248, 242);">.</span>reduce<span style="color: rgb(248, 248, 242);">(</span>async <span style="color: rgb(248, 248, 242);">(</span>_<span style="color: rgb(248, 248, 242);">,</span> doc<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> await _<span style="color: rgb(248, 248, 242);">;</span><br> await db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">},</span> undefined<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面例子中,reduce方法的第一个参数是async函数,导致该函数的第一个参数是前一步操作返回的 Promise 对象,所以必须使用await等待它操作结束。另外,reduce方法返回的是docs数组最后一个成员的async函数的执行结果,也是一个 Promise 对象,导致在它前面也必须加上await。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">如果确实希望多个请求并发执行,可以使用Promise.all方法。当三个请求都会resolved时,下面两种写法效果相同。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br> <span style="color: rgb(102, 217, 239);">let</span> promises <span style="color: rgb(249, 38, 114);">=</span> docs<span style="color: rgb(248, 248, 242);">.</span>map<span style="color: rgb(248, 248, 242);">((</span>doc<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(249, 38, 114);">=></span> db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">));</span><br><br> <span style="color: rgb(102, 217, 239);">let</span> results <span style="color: rgb(249, 38, 114);">=</span> await Promise<span style="color: rgb(248, 248, 242);">.</span>all<span style="color: rgb(248, 248, 242);">(</span>promises<span style="color: rgb(248, 248, 242);">);</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>results<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 或者使用下面的写法<br></span><br>async <span style="color: rgb(102, 217, 239);">function</span> dbFuc<span style="color: rgb(248, 248, 242);">(</span>db<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> docs <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[{},</span> <span style="color: rgb(248, 248, 242);">{},</span> <span style="color: rgb(248, 248, 242);">{}];</span><br> <span style="color: rgb(102, 217, 239);">let</span> promises <span style="color: rgb(249, 38, 114);">=</span> docs<span style="color: rgb(248, 248, 242);">.</span>map<span style="color: rgb(248, 248, 242);">((</span>doc<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(249, 38, 114);">=></span> db<span style="color: rgb(248, 248, 242);">.</span>post<span style="color: rgb(248, 248, 242);">(</span>doc<span style="color: rgb(248, 248, 242);">));</span><br><br> <span style="color: rgb(102, 217, 239);">let</span> results <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">[];</span><br> <span style="color: rgb(102, 217, 239);">for</span> <span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> promise of promises<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> results<span style="color: rgb(248, 248, 242);">.</span>push<span style="color: rgb(248, 248, 242);">(</span>await promise<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>results<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">第四点,async 函数可以保留运行堆栈。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">const a <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> b<span style="color: rgb(248, 248, 242);">().</span>then<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> c<span style="color: rgb(248, 248, 242);">());</span><br><span style="color: rgb(248, 248, 242);">};</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,函数a内部运行了一个异步任务b()。当b()运行的时候,函数a()不会中断,而是继续执行。等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。如果b()或c()报错,错误堆栈将不包括a()。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">现在将这个例子改成async函数。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">const a <span style="color: rgb(249, 38, 114);">=</span> async <span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> await b<span style="color: rgb(248, 248, 242);">();</span><br> c<span style="color: rgb(248, 248, 242);">();</span><br><span style="color: rgb(248, 248, 242);">};</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,b()运行的时候,a()是暂停执行,上下文环境都保存着。一旦b()或c()报错,错误堆栈将包括a()。</span></section><h2 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong><span style="color: rgb(26, 177, 204);">4、async 函数的实现原理</span></strong></h2><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> fn<span style="color: rgb(248, 248, 242);">(</span>args<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // ...<br></span><span style="color: rgb(248, 248, 242);">}</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 等同于<br></span><br><span style="color: rgb(102, 217, 239);">function</span> fn<span style="color: rgb(248, 248, 242);">(</span>args<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> spawn<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(249, 38, 114);">*</span> <span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // ...<br></span> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">所有的async函数都可以写成上面的第二种形式,其中的spawn函数就是自动执行器。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">下面给出spawn函数的实现,基本就是前文自动执行器的翻版。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> spawn<span style="color: rgb(248, 248, 242);">(</span>genF<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> <span style="color: rgb(102, 217, 239);">new</span> Promise<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">(</span>resolve<span style="color: rgb(248, 248, 242);">,</span> reject<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> const gen <span style="color: rgb(249, 38, 114);">=</span> genF<span style="color: rgb(248, 248, 242);">();</span><br> <span style="color: rgb(102, 217, 239);">function</span> step<span style="color: rgb(248, 248, 242);">(</span>nextF<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> next<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> next <span style="color: rgb(249, 38, 114);">=</span> nextF<span style="color: rgb(248, 248, 242);">();</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> reject<span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">if</span><span style="color: rgb(248, 248, 242);">(</span>next<span style="color: rgb(248, 248, 242);">.</span>done<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> resolve<span style="color: rgb(248, 248, 242);">(</span>next<span style="color: rgb(248, 248, 242);">.</span>value<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">(</span>next<span style="color: rgb(248, 248, 242);">.</span>value<span style="color: rgb(248, 248, 242);">).</span>then<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> step<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> gen<span style="color: rgb(248, 248, 242);">.</span>next<span style="color: rgb(248, 248, 242);">(</span>v<span style="color: rgb(248, 248, 242);">);</span> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">},</span> <span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> step<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> gen<span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">throw</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">);</span> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> step<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> gen<span style="color: rgb(248, 248, 242);">.</span>next<span style="color: rgb(248, 248, 242);">(</span>undefined<span style="color: rgb(248, 248, 242);">);</span> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><h2 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong><span style="color: rgb(26, 177, 204);">5、与其他异步处理方法的比较</span></strong></h2><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">我们通过一个例子,来看 async 函数与 Promise、Generator 函数的比较。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">首先是 Promise 的写法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> chainAnimationsPromise<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">,</span> animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 变量ret用来保存上一个动画的返回值<br></span> <span style="color: rgb(102, 217, 239);">let</span> ret <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(102, 217, 239);">null</span><span style="color: rgb(248, 248, 242);">;</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 新建一个空的Promise<br></span> <span style="color: rgb(102, 217, 239);">let</span> p <span style="color: rgb(249, 38, 114);">=</span> Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">();</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 使用then方法,添加所有动画<br></span> <span style="color: rgb(102, 217, 239);">for</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> anim of animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> p <span style="color: rgb(249, 38, 114);">=</span> p<span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">(</span>val<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> ret <span style="color: rgb(249, 38, 114);">=</span> val<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(102, 217, 239);">return</span> anim<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">});</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 返回一个部署了错误捕捉机制的Promise<br></span> <span style="color: rgb(102, 217, 239);">return</span> p<span style="color: rgb(248, 248, 242);">.</span><span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);">/* 忽略错误,继续执行 */</span><br> <span style="color: rgb(248, 248, 242);">}).</span>then<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> ret<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(then、catch等等),操作本身的语义反而不容易看出来。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">接着是 Generator 函数的写法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> chainAnimationsGenerator<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">,</span> animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br><br> <span style="color: rgb(102, 217, 239);">return</span> spawn<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">function</span><span style="color: rgb(249, 38, 114);">*</span><span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> ret <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(102, 217, 239);">null</span><span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">for</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> anim of animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> ret <span style="color: rgb(249, 38, 114);">=</span> yield anim<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);">/* 忽略错误,继续执行 */</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">return</span> ret<span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码使用 Generator 函数遍历了每个动画,语义比 Promise 写法更清晰,用户定义的操作全部都出现在spawn函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行 Generator 函数,上面代码的spawn函数就是自动执行器,它返回一个 Promise 对象,而且必须保证yield语句后面的表达式,必须返回一个 Promise。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">最后是 async 函数的写法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> chainAnimationsAsync<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">,</span> animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">let</span> ret <span style="color: rgb(249, 38, 114);">=</span> <span style="color: rgb(102, 217, 239);">null</span><span style="color: rgb(248, 248, 242);">;</span><br> <span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">for</span><span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(102, 217, 239);">let</span> anim of animations<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> ret <span style="color: rgb(249, 38, 114);">=</span> await anim<span style="color: rgb(248, 248, 242);">(</span>elem<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span><span style="color: rgb(248, 248, 242);">(</span>e<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);">/* 忽略错误,继续执行 */</span><br> <span style="color: rgb(248, 248, 242);">}</span><br> <span style="color: rgb(102, 217, 239);">return</span> ret<span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">可以看到 Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。如果使用 Generator 写法,自动执行器需要用户自己提供。</span></section><h2 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong><span style="color: rgb(26, 177, 204);">6、实例:按顺序完成异步操作</span></strong></h2><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">Promise 的写法如下。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span style="color: rgb(102, 217, 239);">function</span> logInOrder<span style="color: rgb(248, 248, 242);">(</span>urls<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 远程读取所有URL<br></span> const textPromises <span style="color: rgb(249, 38, 114);">=</span> urls<span style="color: rgb(248, 248, 242);">.</span>map<span style="color: rgb(248, 248, 242);">(</span>url <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">).</span>then<span style="color: rgb(248, 248, 242);">(</span>response <span style="color: rgb(249, 38, 114);">=></span> response<span style="color: rgb(248, 248, 242);">.</span>text<span style="color: rgb(248, 248, 242);">());</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 按次序输出<br></span> textPromises<span style="color: rgb(248, 248, 242);">.</span>reduce<span style="color: rgb(248, 248, 242);">((</span>chain<span style="color: rgb(248, 248, 242);">,</span> textPromise<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">return</span> chain<span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> textPromise<span style="color: rgb(248, 248, 242);">)</span><br> <span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(</span>text <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>text<span style="color: rgb(248, 248, 242);">));</span><br> <span style="color: rgb(248, 248, 242);">},</span> Promise<span style="color: rgb(248, 248, 242);">.</span>resolve<span style="color: rgb(248, 248, 242);">());</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码使用fetch方法,同时远程读取一组 URL。每个fetch操作都返回一个 Promise 对象,放入textPromises数组。然后,reduce方法依次处理每个 Promise 对象,然后使用then,将所有 Promise 对象连起来,因此就可以依次输出结果。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">这种写法不太直观,可读性比较差。下面是 async 函数实现。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> logInOrder<span style="color: rgb(248, 248, 242);">(</span>urls<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span style="color: rgb(102, 217, 239);">for</span> <span style="color: rgb(248, 248, 242);">(</span>const url of urls<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> const response <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>await response<span style="color: rgb(248, 248, 242);">.</span>text<span style="color: rgb(248, 248, 242);">());</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码确实大大简化,问题是所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。我们需要的是并发发出远程请求。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;">async <span style="color: rgb(102, 217, 239);">function</span> logInOrder<span style="color: rgb(248, 248, 242);">(</span>urls<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 并发读取远程URL<br></span> const textPromises <span style="color: rgb(249, 38, 114);">=</span> urls<span style="color: rgb(248, 248, 242);">.</span>map<span style="color: rgb(248, 248, 242);">(</span>async url <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> const response <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(102, 217, 239);">return</span> response<span style="color: rgb(248, 248, 242);">.</span>text<span style="color: rgb(248, 248, 242);">();</span><br> <span style="color: rgb(248, 248, 242);">});</span><br><br> <span spellcheck="true" style="color: rgb(117, 113, 94);"> // 按次序输出<br></span> <span style="color: rgb(102, 217, 239);">for</span> <span style="color: rgb(248, 248, 242);">(</span>const textPromise of textPromises<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>await textPromise<span style="color: rgb(248, 248, 242);">);</span><br> <span style="color: rgb(248, 248, 242);">}</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,虽然map方法的参数是async函数,但它是并发执行的,因为只有async函数内部是继发执行,外部不受影响。后面的for..of循环内部使用了await,因此实现了按顺序输出。</span></section><h2 style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><strong><span style="color: rgb(26, 177, 204);">7、顶层 await</span></strong></h2><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">根据语法规格,await命令只能出现在 async 函数内部,否则都会报错。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// 报错<br></span>const data <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>'https://api.example.com'<span style="color: rgb(248, 248, 242);">);</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,await命令独立使用,没有放在 async 函数里面,就会报错。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">目前,有一个语法提案,允许在模块的顶层独立使用await命令,使得上面那行代码不会报错了。这个提案的目的,是借用await解决模块异步加载的问题。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// awaiting.js<br></span><span style="color: rgb(102, 217, 239);">let</span> output<span style="color: rgb(248, 248, 242);">;</span><br>async <span style="color: rgb(102, 217, 239);">function</span> main<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> const dynamic <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>someMission<span style="color: rgb(248, 248, 242);">);</span><br> const data <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br> output <span style="color: rgb(249, 38, 114);">=</span> someProcess<span style="color: rgb(248, 248, 242);">(</span>dynamic<span style="color: rgb(248, 248, 242);">.</span>default<span style="color: rgb(248, 248, 242);">,</span> data<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br>main<span style="color: rgb(248, 248, 242);">();</span><br>export <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">};</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,模块awaiting.js的输出值output,取决于异步操作。我们把异步操作包装在一个 async 函数里面,然后调用这个函数,只有等里面的异步操作都执行,变量output才会有值,否则就返回undefined。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面的代码也可以写成立即执行函数的形式。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// awaiting.js<br></span><span style="color: rgb(102, 217, 239);">let</span> output<span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(248, 248, 242);">(</span>async function1 main<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> const dynamic <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>someMission<span style="color: rgb(248, 248, 242);">);</span><br> const data <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br> output <span style="color: rgb(249, 38, 114);">=</span> someProcess<span style="color: rgb(248, 248, 242);">(</span>dynamic<span style="color: rgb(248, 248, 242);">.</span>default<span style="color: rgb(248, 248, 242);">,</span> data<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">})();</span><br>export <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">};</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">下面是加载这个模块的写法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// usage.js<br></span>import <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">}</span> from "./awaiting.js"<span style="color: rgb(248, 248, 242);">;</span><br><br><span style="color: rgb(102, 217, 239);">function</span> outputPlusValue<span style="color: rgb(248, 248, 242);">(</span>value<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> output <span style="color: rgb(249, 38, 114);">+</span> value <span style="color: rgb(248, 248, 242);">}</span><br><br>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">));</span><br>setTimeout<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">),</span> <span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">);</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,outputPlusValue()的执行结果,完全取决于执行的时间。如果awaiting.js里面的异步操作没执行完,加载进来的output的值就是undefined。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">目前的解决方法,就是让原始模块输出一个 Promise 对象,从这个 Promise 对象判断异步操作有没有结束。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// awaiting.js<br></span><span style="color: rgb(102, 217, 239);">let</span> output<span style="color: rgb(248, 248, 242);">;</span><br>export default <span style="color: rgb(248, 248, 242);">(</span>async <span style="color: rgb(102, 217, 239);">function</span> main<span style="color: rgb(248, 248, 242);">()</span> <span style="color: rgb(248, 248, 242);">{</span><br> const dynamic <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>someMission<span style="color: rgb(248, 248, 242);">);</span><br> const data <span style="color: rgb(249, 38, 114);">=</span> await fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br> output <span style="color: rgb(249, 38, 114);">=</span> someProcess<span style="color: rgb(248, 248, 242);">(</span>dynamic<span style="color: rgb(248, 248, 242);">.</span>default<span style="color: rgb(248, 248, 242);">,</span> data<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">})();</span><br>export <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">};</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,awaiting.js除了输出output,还默认输出一个 Promise 对象(async 函数立即执行后,返回一个 Promise 对象),从这个对象判断异步操作是否结束。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">下面是加载这个模块的新的写法。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// usage.js<br></span>import promise<span style="color: rgb(248, 248, 242);">,</span> <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">}</span> from "./awaiting.js"<span style="color: rgb(248, 248, 242);">;</span><br><br><span style="color: rgb(102, 217, 239);">function</span> outputPlusValue<span style="color: rgb(248, 248, 242);">(</span>value<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> output <span style="color: rgb(249, 38, 114);">+</span> value <span style="color: rgb(248, 248, 242);">}</span><br><br>promise<span style="color: rgb(248, 248, 242);">.</span>then<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> <span style="color: rgb(248, 248, 242);">{</span><br> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">));</span><br> setTimeout<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">),</span> <span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">});</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,将awaiting.js对象的输出,放在promise.then()里面,这样就能保证异步操作完成以后,才去读取output。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">这种写法比较麻烦,等于要求模块的使用者遵守一个额外的使用协议,按照特殊的方法使用这个模块。一旦你忘了要用 Promise 加载,只使用正常的加载方法,依赖这个模块的代码就可能出错。而且,如果上面的usage.js又有对外的输出,等于这个依赖链的所有模块都要使用 Promise 加载。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">顶层的await命令,就是为了解决这个问题。它保证只有异步操作完成,模块才会输出值。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// awaiting.js<br></span>const dynamic <span style="color: rgb(249, 38, 114);">=</span> import<span style="color: rgb(248, 248, 242);">(</span>someMission<span style="color: rgb(248, 248, 242);">);</span><br>const data <span style="color: rgb(249, 38, 114);">=</span> fetch<span style="color: rgb(248, 248, 242);">(</span>url<span style="color: rgb(248, 248, 242);">);</span><br>export const output <span style="color: rgb(249, 38, 114);">=</span> someProcess<span style="color: rgb(248, 248, 242);">((</span>await dynamic<span style="color: rgb(248, 248, 242);">).</span>default<span style="color: rgb(248, 248, 242);">,</span> await data<span style="color: rgb(248, 248, 242);">);</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码中,两个异步操作在输出的时候,都加上了await命令。只有等到异步操作完成,这个模块才会输出值。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">加载这个模块的写法如下。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// usage.js<br></span>import <span style="color: rgb(248, 248, 242);">{</span> output <span style="color: rgb(248, 248, 242);">}</span> from "./awaiting.js"<span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(102, 217, 239);">function</span> outputPlusValue<span style="color: rgb(248, 248, 242);">(</span>value<span style="color: rgb(248, 248, 242);">)</span> <span style="color: rgb(248, 248, 242);">{</span> <span style="color: rgb(102, 217, 239);">return</span> output <span style="color: rgb(249, 38, 114);">+</span> value <span style="color: rgb(248, 248, 242);">}</span><br><br>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">));</span><br>setTimeout<span style="color: rgb(248, 248, 242);">(()</span> <span style="color: rgb(249, 38, 114);">=></span> console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>outputPlusValue<span style="color: rgb(248, 248, 242);">(</span><span style="color: rgb(174, 129, 255);">100</span><span style="color: rgb(248, 248, 242);">),</span> <span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">);</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码的写法,与普通的模块加载完全一样。也就是说,模块的使用者完全不用关心,依赖模块的内部有没有异步操作,正常加载即可。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">这时,模块的加载会等待依赖模块(上例是awaiting.js)的异步操作完成,才执行后面的代码,有点像暂停在那里。所以,它总是会得到正确的output,不会因为加载时机的不同,而得到不一样的值。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">注意,顶层await只能用在 ES6 模块,不能用在 CommonJS 模块。这是因为 CommonJS 模块的require()是同步加载,如果有顶层await,就没法处理加载了。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">下面是顶层await的一些使用场景。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// import() 方法加载<br></span>const strings <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>`<span style="color: rgb(249, 38, 114);">/</span>i18n<span style="color: rgb(249, 38, 114);">/</span>$<span style="color: rgb(248, 248, 242);">{</span>navigator<span style="color: rgb(248, 248, 242);">.</span>language<span style="color: rgb(248, 248, 242);">}</span>`<span style="color: rgb(248, 248, 242);">);</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 数据库操作<br></span>const connection <span style="color: rgb(249, 38, 114);">=</span> await dbConnector<span style="color: rgb(248, 248, 242);">();</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// 依赖回滚<br></span><span style="color: rgb(102, 217, 239);">let</span> jQuery<span style="color: rgb(248, 248, 242);">;</span><br><span style="color: rgb(102, 217, 239);">try</span> <span style="color: rgb(248, 248, 242);">{</span><br> jQuery <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>'https://cdn-a.com/jQuery'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span> <span style="color: rgb(102, 217, 239);">catch</span> <span style="color: rgb(248, 248, 242);">{</span><br> jQuery <span style="color: rgb(249, 38, 114);">=</span> await import<span style="color: rgb(248, 248, 242);">(</span>'https://cdn-b.com/jQuery'<span style="color: rgb(248, 248, 242);">);</span><br><span style="color: rgb(248, 248, 242);">}</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">注意,如果加载多个包含顶层await命令的模块,加载命令是同步执行的。</span></section><pre style="margin: 0.5em auto;padding: 10px 1em 10px 0.7rem;overflow: auto;font-family: Consolas, Monaco, "Andale Mono", monospace;font-size: 15.36px;color: rgb(255, 255, 255);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;text-align: left;word-break: normal;tab-size: 4;hyphens: none;border-radius: 5px;background: rgb(17, 17, 17);line-height: 1.2;"><section style="margin-left: 8px;margin-right: 8px;"><code style="font-family: Consolas, "Courier New", Courier, FreeMono, monospace;font-size: 0.7rem;color: rgb(166, 226, 46);text-shadow: rgba(0, 0, 0, 0.3) 0px 1px;direction: ltr;word-spacing: normal;word-break: normal;tab-size: 4;hyphens: none;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-radius: 2px;"><span spellcheck="true" style="color: rgb(117, 113, 94);">// x.js<br></span>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>"X1"<span style="color: rgb(248, 248, 242);">);</span><br>await <span style="color: rgb(102, 217, 239);">new</span> Promise<span style="color: rgb(248, 248, 242);">(</span>r <span style="color: rgb(249, 38, 114);">=></span> setTimeout<span style="color: rgb(248, 248, 242);">(</span>r<span style="color: rgb(248, 248, 242);">,</span> <span style="color: rgb(174, 129, 255);">1000</span><span style="color: rgb(248, 248, 242);">));</span><br>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>"X2"<span style="color: rgb(248, 248, 242);">);</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// y.js<br></span>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>"Y"<span style="color: rgb(248, 248, 242);">);</span><br><span spellcheck="true" style="color: rgb(117, 113, 94);"><br>// z.js<br></span>import "./x.js"<span style="color: rgb(248, 248, 242);">;</span><br>import "./y.js"<span style="color: rgb(248, 248, 242);">;</span><br>console<span style="color: rgb(248, 248, 242);">.</span>log<span style="color: rgb(248, 248, 242);">(</span>"Z"<span style="color: rgb(248, 248, 242);">);</span><br></code></section></pre><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">上面代码有三个模块,最后的z.js加载x.js和y.js,打印结果是X1、Y、X2、Z。这说明,z.js并没有等待x.js加载完成,再去加载y.js。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">顶层的await命令有点像,交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,继续向下执行。</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">本文完~</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><span style="font-size: 16px;">推荐阅读</span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;"><span style="font-size: 16px;"><a target="_blank" data-itemshowtype="0" tab="innerlink" data-linktype="2">ECMAScript 6 入门教程—Iterator 和 for...of 循环</a><br></span></section><section style="margin: 15px 8px;white-space: normal;text-align: left;"><a target="_blank" data-itemshowtype="0" tab="innerlink" data-linktype="2">ECMAScript 6 入门教程—Promise 对象</a><br></section><section style="margin: 15px 8px;white-space: normal;text-align: left;"><a target="_blank" data-itemshowtype="0" tab="innerlink" data-linktype="2">ECMAScript 6 入门教程—Reflect</a><br></section><section style="margin: 15px 8px;white-space: normal;text-align: left;"><a target="_blank" data-itemshowtype="0" tab="innerlink" data-linktype="2">ECMAScript 6 入门教程—Proxy</a><br></section><section style="margin: 15px 8px;white-space: normal;text-align: left;"><a target="_blank" data-itemshowtype="0" tab="innerlink" data-linktype="2">ECMAScript 6 入门教程—Set 和 Map 数据结构</a></section><section style="margin: 15px 8px;white-space: normal;text-align: left;line-height: 1.75em;"><br></section><section style="white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: left;margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-ratio="0.044444444444444446" data-s="300,640" data-type="jpeg" data-w="900" src="https://filescdn.proginn.com/e6e44f6367daa117943164d48f543304/dd4f34d9c3f980dddc665a070895ac56.webp" style="box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"></section><section style="white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: left;margin-left: 8px;margin-right: 8px;"></section> </div></div></div><div class="tag-list-box"><div class="tag-list"><div class="tag-list-container"></div></div></div><span class="view_num">浏览 1</span><div class="float-bar float-bar-relative" id="float-bar-relative"><div class="float-bar-body"><div class="item qinglite-zan"><i class="iconfont icon-dianzan"></i>点赞 </div><div class="gap"></div><a href="#comments" class="item"><i class="iconfont iconfont icon-pinglun1"></i><span class="com_num"></span>评论 </a><div class="item qinglite-collect"><i class="iconfont icon-shoucang"></i>收藏 </div><div class="item qinglite_share"><i class="iconfont icon-fenxiang1"></i>分享 <div class="qrcode-modal"><img src="/api/pub/ewm" alt=""><p>手机扫一扫分享</p></div></div><div class="expand"></div><a onclick="miniProgram_navigateTo_func()" class="item qinglite_share_miniapp miniapp_show"><i class="iconfont icon-fenxiang1"></i>分享 </a><div class="item jubao qinglite-jubao miniapp_hide"><i class="iconfont icon-jubao"></i> 举报 </div></div></div></div><div class="comments_wrapper comments"><div class="title">评论</div><div id="comments" class="comments"><div class="error"></div><div class="textarea-wrapper"><textarea class="comment-content" cols="30" rows="5" placeholder="输入评论"></textarea></div><div class="button"><div class="error"><div class="comment-emojis"><div class="comment-choose-img qinglite_upload"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-tupianyangshi2"></use></svg><span>图片</span></div><div class="comment-choose-img comment-emoji-btn"><svg class="icon show-emoji-list" aria-hidden="true"><use xlink:href="#icon-biaoqing"></use></svg><span class="show-emoji-list">表情</span><div class="comment-emoji-list"></div></div><div style="display: none" class="comment-choose-img"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-shipinwenjian1"></use></svg><span>视频</span></div></div></div><button class="qinglite-comment">评价</button></div><div class="medias qinglite_upload_content"></div></div></div><div style="display: none" class="comments"><div class="title">全部评论</div><div class="comments comment-item-content"></div></div><div id="recommend" class="comments hide_app"><div class="title">推荐 <a href="#qs_detail" class="iconfont icon-huidaodingbu"></a></div></div><div class="qs_post_list flow_post_list hide_app"><div class="item img qinglite_item"><a href="/doc/28916459f1531ab03" title="「译」更快的 async 函数和 promises" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/ead5b91a0f7a290f1840108a6a799a3a/ce6041b20f8dde0156ea290281753f02.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/28916459f1531ab03" title="「译」更快的 async 函数和 promises" class="title_middle">「译」更快的 async 函数和 promises</a><a href="https://jishu.proginn.com/u/6996644331d0693ba" title="程序员成长指北" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/462560/cps_wx_0173a365bec2.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">程序员成长指北</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item img qinglite_item"><a href="/doc/45966463b566c4520" title="「译」更快的 async 函数和 promises" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/f06b953d8cc1e83a3bbffe55469665b7/c4d7942f8b2dd87b3bb40261ca1ca91b.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/45966463b566c4520" title="「译」更快的 async 函数和 promises" class="title_middle">「译」更快的 async 函数和 promises</a><a href="https://jishu.proginn.com/u/2502644d5f5acafe4" title="全栈前端精选" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/477636/cps_wx_01745c891394.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">全栈前端精选</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item qinglite_item qinglite_item_news"><a href="/pedia/f0056278651a70b0efc75" title="Async" class="content"><div class="qinglite_item_top_wrapper"><div class="title">Async</div><div class="right-top-icon-tag"></div></div><div class="desc">Thispluginallowstoeasilycodeverylong-lastingloopsinanasynchronouswaytoavoidlosingthebrowserresponsiv</div></a></div><div class="item img qinglite_item"><a href="/doc/5709647598028c19a" title="【JS】930- 更快的 async 函数和 promises" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/106220fc3003aad51affd4743cb91033/10e9343d8b64f45658296f8f355aa5d9.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/5709647598028c19a" title="【JS】930- 更快的 async 函数和 promises" class="title_middle">【JS】930- 更快的 async 函数和 promises</a><a href="https://jishu.proginn.com/u/63446447295dc213d" title="前端自习课" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/465980/cps_wx_0173a36605ea.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">前端自习课</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item img qinglite_item"><a href="/doc/6598647576e81dc11" title="Flutter异步编程-sync*和async*生成器函数" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/868fb03131882944900749479c438d95/6a2a05c7272a303d4abc5c4666500c53.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/6598647576e81dc11" title="Flutter异步编程-sync*和async*生成器函数" class="title_middle">Flutter异步编程-sync*和async*生成器函数</a><a href="https://jishu.proginn.com/u/484264756bf742e2c" title="熊喵先生" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/574600/cps_wx_01788d3de10b.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">熊喵先生</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item qinglite_item qinglite_item_news"><a href="/pedia/f005714465145025a1385" title="ECMAScript脚本程序设计语言" class="content"><div class="qinglite_item_top_wrapper"><div class="title">ECMAScript脚本程序设计语言</div><div class="right-top-icon-tag"></div></div><div class="desc">ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际</div></a></div><div class="item qinglite_item qinglite_item_news"><a href="/doc/f00513936514502698297" title="ECMAScript脚本程序设计语言" class="content"><div class="qinglite_item_top_wrapper"><div class="title">ECMAScript脚本程序设计语言</div><div class="right-top-icon-tag"></div></div><div class="desc">ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)通过ECMA-262标准化的脚</div></a></div><div class="item img qinglite_item"><a href="/doc/891564587ff299075" title="巨大提升!更快的 async 函数和 promises「译」" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/e2c7038174e3497fd6aa096e649b3480/f8d404f21ebb1ff94914e39fa1d8bc61.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/891564587ff299075" title="巨大提升!更快的 async 函数和 promises「译」" class="title_middle">巨大提升!更快的 async 函数和 promises「译」</a><a href="https://jishu.proginn.com/u/76656445886600931" title="前端下午茶" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/465979/cps_wx_0173a36604e1.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">前端下午茶</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item img qinglite_item"><a href="/doc/1366647686866f9fd" title="面试官问 async、await 函数原理是在问什么?" class="content"><div class="bg" style="background-image:url(https://filescdn.proginn.com/40736fb9d82d629bec42c22fed72d056/b9e260e56722864222d6af79a194a1e1.webp?x-oss-process=image/resize,w_300)"></div></a><a href="/doc/1366647686866f9fd" title="面试官问 async、await 函数原理是在问什么?" class="title_middle">面试官问 async、await 函数原理是在问什么?</a><a href="https://jishu.proginn.com/u/48216474f4d32e421" title="前端Sharing" class="up_info"><div style="background-image:url(https://inn.proginn.com/useralbum/547998/cps_wx_0177d7ea325e.jpg!mediumicon?imageMogr2/format/webp/thumbnail/!200x200r)" class="avatar"></div><div class="username">前端Sharing</div><div class="expand"></div><div class="likes"><i class="iconfont icon-dianzan"></i></div><span class="num">0</span></a></div><div class="item qinglite_item qinglite_item_news"><a href="/doc/f005384965126488b1648" title="WisdomPlanet-Javascript-PrimerJavaScript 入门教程" class="content"><div class="qinglite_item_top_wrapper"><div class="title">WisdomPlanet-Javascript-PrimerJavaScript 入门教程</div><div class="right-top-icon-tag"></div></div><div class="desc">WisdomPlanet-Javascript-Primer 是智慧星球 Javascript 入门</div></a></div><i></i><i></i><i></i><i></i><i></i></div><div class="float-bar" id="float-bar"><div class="float-bar-body"><div class="item qinglite-zan"><i class="iconfont icon-dianzan"></i>点赞 </div><div class="gap"></div><a href="#comments" class="item"><i class="iconfont iconfont icon-pinglun1"></i><span class="com_num"></span>评论 </a><div class="item qinglite-collect"><i class="iconfont icon-shoucang"></i>收藏 </div><div class="item qinglite_share"><i class="iconfont icon-fenxiang1"></i>分享 <div class="qrcode-modal"><img src="/api/pub/ewm" alt=""><p>手机扫一扫分享</p></div></div><div class="expand"></div><a onclick="miniProgram_navigateTo_func()" class="item qinglite_share_miniapp miniapp_show"><i class="iconfont icon-fenxiang1"></i>分享 </a><div class="item jubao qinglite-jubao miniapp_hide"><i class="iconfont icon-jubao"></i> 举报 </div><a href="#recommend" class="item iconfont icon-huidaodingbu"></a></div></div></article></div></main><script> let act_type = 1; let act_pro_id="57396"; let act_point = 0; let act_kind = 0; let act_time =210000; let act_page_id=""; </script><footer id="footer"><div class="container"><div class="links"><i class="copyright">2023©技术圈</i><a href="https://jishu.proginn.com">隐私协议</a><a href="https://jishu.proginn.com">用户协议</a><a href="https://jishu.proginn.com/about">关于我们</a></div></div></footer><link href="https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/css/layui/css/layui.css" rel="stylesheet"><script src="https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/css/layui/layui.js?v=v202311290135"></script><script> var $ = layui.jquery; </script><script src="https://cdn.qinglite.cn/js/pub.js?v=v202311290135"></script><script src="https://cdn.qinglite.cn/js/news_info.js?v=v202311290135"></script><link rel="stylesheet" href="https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/css/icon/iconfont.css?v=v202311290135"><script src="https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/css/icon/iconfont.js?v=v202311290135"></script></body></html>