原文連接:https://blog.csdn.net/sinat_17775997/article/details/76481923
async(異步) 函數(shù)變體
以下是已經(jīng)存在的異步函數(shù)變體史侣。請(qǐng)注意無(wú)處不在的 async
關(guān)鍵字。
- 異步函數(shù)聲明:
async function foo() {}
- 異步函數(shù)表達(dá)式:
const foo = async function () {};
- 異步函數(shù)定義:
let obj = { async foo() {} }
- 異步箭頭函數(shù):
const foo = async () => {};
async(異步) 函數(shù)總是返回 Promises
async(異步) 函數(shù)的 Promise 完成狀態(tài):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
return 123;
}
asyncFunc()
.then(x => console.log(x));
// 123
</pre>
async(異步) 函數(shù)的 Promise 拒絕狀態(tài):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
throw new Error('Problem!');
}
asyncFunc()
.catch(err => console.log(err));
// Error: Problem!
</pre>
通過(guò) await
處理 async(異步) 計(jì)算的結(jié)果和錯(cuò)誤
await
(只允許在 async(異步) 函數(shù)內(nèi)部使用)等待其操作對(duì)象 Promise 返回:
- 如果 Promise 是完成狀態(tài)魏身,
await
的結(jié)果是完成態(tài)的值惊橱。 - 如果 Promise 是拒絕狀態(tài),
await
會(huì)拋出拒絕值箭昵。
處理單個(gè) async(異步) 返回值:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
const result = await otherAsyncFunc();
console.log(result);
}
// 等價(jià)于:
function asyncFunc() {
return otherAsyncFunc()
.then(result => {
console.log(result);
});
}
</pre>
按順序處理多個(gè) async(異步) 返回值:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
const result1 = await otherAsyncFunc1();
console.log(result1);
const result2 = await otherAsyncFunc2();
console.log(result2);
}
// 等價(jià)于:
function asyncFunc() {
return otherAsyncFunc1()
.then(result1 => {
console.log(result1);
return otherAsyncFunc2();
})
.then(result2 => {
console.log(result2);
});
}
</pre>
并行處理多個(gè) async(異步) 返回值:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
const [result1, result2] = await Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
]);
console.log(result1, result2);
}
// 等價(jià)于:
function asyncFunc() {
return Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
])
.then([result1, result2] => {
console.log(result1, result2);
});
}
</pre>
錯(cuò)誤處理:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
try {
await otherAsyncFunc();
} catch (err) {
console.error(err);
}
}
// 等價(jià)于:
function asyncFunc() {
return otherAsyncFunc()
.catch(err => {
console.error(err);
});
}
</pre>
理解 async(異步) 函數(shù)
在我解釋 async(異步) 函數(shù)之前税朴,我需要解釋一下如何組合使用 Promises 和 Generator ,通過(guò)看起來(lái)同步的代碼來(lái)執(zhí)行 async(異步) 操作家制。
對(duì)于能夠 async(異步) 計(jì)算其一次性結(jié)果的函數(shù)正林,作為 ES6 一部分的 Promises 已經(jīng)變得流行起來(lái)。一個(gè)例子是 客戶端 fetch API 颤殴,它是 XMLHttpRequest 獲取數(shù)據(jù)的替代方法觅廓。使用示例如下:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- function fetchJson(url) {
- return fetch(url)
- .then(request => request.text())
- .then(text => {
- return JSON.parse(text);
- })
- .catch(error => {
- console.log(
ERROR: ${error.stack}
); - });
- }
- fetchJson('http://example.com/some_file.json')
- .then(obj => console.log(obj));
</pre>
通過(guò) generator 來(lái)編寫(xiě)異步代碼
co 是一個(gè)使用 Promise 和 generator 來(lái)實(shí)現(xiàn)看似同步編碼的庫(kù),但與上一示例中使用的樣式相同:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- const fetchJson = co.wrap(function* (url) {
- try {
- let request = yield fetch(url);
- let text = yield request.text();
- return JSON.parse(text);
- }
- catch (error) {
- console.log(
ERROR: ${error.stack}
); - }
- });
</pre>
每次回調(diào)函數(shù)( generator 函數(shù))產(chǎn)生一個(gè) Promise 對(duì)象給 co 诅病,回調(diào)會(huì)被暫停哪亿,只有當(dāng) Promise 執(zhí)行完成后粥烁,co 才會(huì)繼續(xù)執(zhí)行回調(diào) 。 如果 Promise 處于完成狀態(tài)蝇棉,yield
返回完成狀態(tài)的值讨阻,如果處于拒絕狀態(tài),yield
拋出拒絕狀態(tài)的錯(cuò)誤篡殷。此外钝吮,co 保證結(jié)果是通過(guò)回調(diào)執(zhí)行完成才返回的(類似于 then()
所做的工作)。
通過(guò) async(異步) 函數(shù)來(lái)編寫(xiě)異步代碼
async(異步) 函數(shù)用的特定語(yǔ)法基本上和 co 類似:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function fetchJson(url) {
- try {
- let request = await fetch(url);
- let text = await request.text();
- return JSON.parse(text);
- }
- catch (error) {
- console.log(
ERROR: ${error.stack}
); - }
- }
</pre>
在內(nèi)部板辽,異步函數(shù)寫(xiě)法更類似于 generators 奇瘦。
以同步開(kāi)始,異步處理的 async(異步) 函數(shù)
以下是 async(異步)函數(shù)是如何工作的:
- async(異步) 函數(shù)總是返回一個(gè) Promise 對(duì)象
p
劲弦。Promise 對(duì)象在 async(異步) 函數(shù)開(kāi)始執(zhí)行時(shí)被創(chuàng)建耳标。 - 函數(shù)體執(zhí)行過(guò)程中,可以通過(guò)
return
或throw
終止執(zhí)行邑跪〈纹拢或者通過(guò)await
暫停執(zhí)行,在這種情況下画畅,通常會(huì)在以后繼續(xù)執(zhí)行砸琅。 - 返回 Promise 對(duì)象
p
。
當(dāng)執(zhí)行 async(異步) 函數(shù)的函數(shù)體時(shí)轴踱,return x
中的 x
是 Promise 對(duì)象 p
的完成狀態(tài)的結(jié)果症脂,而 throw err
是 p
的拒絕狀態(tài)的結(jié)果。執(zhí)行結(jié)果是異步返回的淫僻。換句話說(shuō):then()
和 catch()
的回調(diào)總是在當(dāng)前代碼完成后執(zhí)行诱篷。
以下是代碼示例:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
async function asyncFunc() {
console.log('asyncFunc()'); // (A)
return 'abc';
}
asyncFunc().
then(x => console.log(
Resolved: ${x}
)); // (B)console.log('main'); // (C)
// Output:
// asyncFunc()
// main
// Resolved: abc
</pre>
您可以認(rèn)為是以下的執(zhí)行順序:
- 行A:async(異步) 函數(shù)以同步開(kāi)始。async(異步) 函數(shù)的 Promise 通過(guò)
return
來(lái)返回完成狀態(tài)的結(jié)果嘁傀。 - 行C:執(zhí)行繼續(xù)兴蒸。
- 行B:Promise 完成狀態(tài)通知是異步發(fā)生的。
返回不被包裹的 Promise 對(duì)象
Promise 的 resolve 是一項(xiàng)標(biāo)準(zhǔn)操作细办。 return
就是使用它來(lái) resolve async(異步) 函數(shù)的 Promise p
的。這意味著:
- 返回一個(gè)非 Promise 值蕾殴,該值將被處理成
p
的完成狀態(tài)值笑撞。 - 返回一個(gè) Promise 對(duì)象,那么
p
此時(shí)相當(dāng)于是該 Promise 的狀態(tài)钓觉。
因此茴肥,您可以返回一個(gè) Promise ,并且這個(gè) Promise 不會(huì)包裹在別的 Promise 中:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- return Promise.resolve(123);
- }
- asyncFunc()
- .then(x => console.log(x)) // 123
</pre>
有趣的是荡灾,返回一個(gè)拒絕狀態(tài)(reject)的 Promise 對(duì)象會(huì)導(dǎo)致 async(異步) 函數(shù)被拒絕(reject)(通常瓤狐,您可以使用 throw
):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- return Promise.reject(new Error('Problem!'));
- }
- asyncFunc()
- .catch(err => console.error(err)); // Error: Problem!
</pre>
這與 Promise 解決方案的工作方式是一致的瞬铸。 使你能夠在不使用 await
的情況下,使用其他 async(異步) 計(jì)算來(lái)執(zhí)行完成和拒絕處理:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- return anotherAsyncFunc();
- }
</pre>
上面的代碼示例和下面的類似础锐,但是比下面的更高效嗓节。(以下代碼示例沒(méi)有包裹 anotherAsyncFunc()
的 Promise ,而是包裹 anotherAsyncFunc()
本身 ):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- return await anotherAsyncFunc();
- }
</pre>
使用 await
小貼士
不要忘記使用 await
在 async(異步) 函數(shù)中容易犯的一個(gè)錯(cuò)誤就是在調(diào)用 async(異步) 函數(shù)時(shí)忘記使用 await
:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- const value = otherAsyncFunc(); // missing
await
! - ···
- }
</pre>
在這個(gè)例子中皆警,方法執(zhí)行返回的 Promise 對(duì)象賦值給了 value
拦宣,它通常不是你在 async(異步) 函數(shù)中想要的結(jié)果。
await 甚至可以在 async(異步) 函數(shù)不返回任何值的情況下起作用信姓。它的 Promise 只是用來(lái)告訴調(diào)用者完成狀態(tài)鸵隧。例如:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function foo() {
- await step1(); // (A)
- ···
- }
</pre>
行A中的 await
確保在執(zhí)行 foo()
剩余部分之前, step1()
已經(jīng)執(zhí)行完成意推。
不需要使用 await 的情況
有時(shí)豆瘫,你只想觸發(fā)異步計(jì)算,并且不需要關(guān)注它什么時(shí)候完成菊值。以下是代碼示例:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- const writer = openFile('someFile.txt');
- writer.write('hello'); // don’t wait
- writer.write('world'); // don’t wait
- await writer.close(); // wait for file to close
- }
</pre>
在這里靡羡,我們不關(guān)心單個(gè)的寫(xiě)入操作是否完成,只需要他們以正確的順序執(zhí)行 (API必須保證俊性,但這是由 async(異步) 函數(shù)的執(zhí)行模型所鼓勵(lì)的略步,正如我們所見(jiàn))。
asyncFunc()
函數(shù)最后一行的 await
確保該函數(shù)僅在文件寫(xiě)入關(guān)閉后才會(huì)執(zhí)行定页。
由于返回的 Promises 沒(méi)有被其他 async(異步) 函數(shù)包裹趟薄,所以你可以用 return
替換 await writer.close()
:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function asyncFunc() {
- const writer = openFile('someFile.txt');
- writer.write('hello');
- writer.write('world');
- return writer.close();
- }
</pre>
這兩個(gè)版本各有利弊,await
版本可能稍微更容易理解典徊。
await 是順序執(zhí)行的杭煎,Promise.all() 是并行的
下面的代碼調(diào)用了兩個(gè) async(異步) 函數(shù), asyncFunc1()
和 asyncFunc1()
卒落。
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function foo() {
- const result1 = await asyncFunc1();
- const result2 = await asyncFunc2();
- }
</pre>
這兩個(gè)函數(shù)調(diào)用順序執(zhí)行羡铲。但是并行執(zhí)行它們往往會(huì)加快速度。您可以使用 Promise.all() :
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function foo() {
- const [result1, result2] = await Promise.all([
- asyncFunc1(),
- asyncFunc2(),
- ]);
- }
</pre>
我們現(xiàn)在正在等待一個(gè)包含兩個(gè)元素的數(shù)組的 Promise 儡毕,而不是等待兩個(gè) Promise也切。
異步函數(shù)和回調(diào)
async(異步) 函數(shù)的一個(gè)限制是 await
(等待) 只影響直接相關(guān)的 async(異步) 函數(shù)。因此腰湾,async(異步) 函數(shù)無(wú)法在回調(diào)(但是雷恃,回調(diào)可以是 async(異步) 函數(shù)本身,稍后我們將會(huì)看到)中使用 await
(等待)费坊。這使得基于回調(diào)的實(shí)用函數(shù)和方法難以使用倒槐。例子中我們將使用數(shù)組方法 map()
和 forEach()
。
Array.prototype.map()
我們從數(shù)組方法 map()
開(kāi)始講解附井。在下面的代碼示例中讨越,我們想要加載由 URLs 數(shù)組指向的文件两残,并將它們返回到數(shù)組中。
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function downloadContent(urls) {
- return urls.map(url => {
- // 錯(cuò)誤的語(yǔ)法!
- const content = await httpGet(url);
- return content;
- });
- }
</pre>
這不起作用把跨,因?yàn)樵谡<^函數(shù)中 await
語(yǔ)法上是非法的(愚人碼頭注: await
(等待) 只影響直接相關(guān)的 async(異步) 函數(shù))人弓。那么如何使用異步的箭頭函數(shù)呢?
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function downloadContent(urls) {
- return urls.map(async (url) => { // 注意這一行中的 async ;
- const content = await httpGet(url);
- return content;
- });
- }
</pre>
這段代碼有兩個(gè)問(wèn)題:
- 現(xiàn)在返回的結(jié)果是一個(gè) Promises 對(duì)象的數(shù)組节猿,而不是一個(gè)字符串的數(shù)組票从。
- 一旦
map()
執(zhí)行完成,回調(diào)執(zhí)行的工作并不能同時(shí)完成滨嘱,因?yàn)?await
只暫停了包裹它的箭頭函數(shù) 和httpGet()
異步執(zhí)行達(dá)到完成狀態(tài)峰鄙。這意味著你不能使用await
,來(lái)等待downloadContent()
執(zhí)行結(jié)束太雨。
我們可以通過(guò) Promise.all()
來(lái)解決這兩個(gè)問(wèn)題吟榴,Promise.all()
可以將一系列的 Promise 轉(zhuǎn)換為一個(gè) Promise 數(shù)組(所有值都是經(jīng)過(guò) Promise 完成并返回):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function downloadContent(urls) {
- const promiseArray = urls.map(async (url) => {
- const content = await httpGet(url);
- return content;
- });
- return await Promise.all(promiseArray);
- }
</pre>
map()
的回調(diào)并不對(duì) httpGet()
的結(jié)果起作用,只是起到不斷執(zhí)行的作用囊扳。因此吩翻,這里我們不需要一個(gè)異步的箭頭函數(shù),只需要一個(gè)普通的箭頭函數(shù)就能達(dá)到相同的結(jié)果锥咸。
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function downloadContent(urls) {
- const promiseArray = urls.map(
- url => httpGet(url));
- return await Promise.all(promiseArray);
- }
</pre>
我們?nèi)匀豢梢宰鲆粋€(gè)小的改進(jìn):這個(gè)異步函數(shù)稍微有點(diǎn)低效 – 首先通過(guò) await
來(lái)解開(kāi) Promise.all()
的結(jié)果狭瞎,然后通過(guò) return
再次包裹它。 假設(shè) return
不包裹 Promises搏予,我們可以直接返回 Promise.all()
的結(jié)果:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function downloadContent(urls) {
- const promiseArray = urls.map(
- url => httpGet(url));
- return Promise.all(promiseArray);
- }
</pre>
Array.prototype.forEach()
我們使用數(shù)組的 forEach() 方法在控制臺(tái)中打印幾個(gè)通過(guò) URLs 加載的文件的內(nèi)容:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function logContent(urls) {
- urls.forEach(url => {
- // Wrong syntax
- const content = await httpGet(url);
- console.log(content);
- });
- }
</pre>
同樣的熊锭,這里的代碼會(huì)產(chǎn)生一個(gè)語(yǔ)法錯(cuò)誤,因?yàn)槟悴荒茉谕ǔ5募^函數(shù)內(nèi)部使用 await
雪侥。
我們換作異步箭頭函數(shù):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function logContent(urls) {
- urls.forEach(async url => {
- const content = await httpGet(url);
- console.log(content);
- });
- // Not finished here
- }
</pre>
這段代碼起作用了碗殷,但是會(huì)出現(xiàn)一個(gè)警告:httpGet()
返回的 Promise 對(duì)象是異步完成的,這也意味著當(dāng) forEach()
返回的時(shí)候回調(diào)可能還沒(méi)有結(jié)束速缨,因此你無(wú)法等到 logContent()
只能結(jié)束锌妻。
如果你并不想要這個(gè)結(jié)果,你可以將 forEach()
轉(zhuǎn)換為 for-of
循環(huán)旬牲。
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function logContent(urls) {
- for (const url of urls) {
- const content = await httpGet(url);
- console.log(content);
- }
- }
</pre>
現(xiàn)在一切都在 for-of
循環(huán)完成后完成仿粹。但是,處理步驟依次發(fā)生:httpGet()
只是在第一次調(diào)用完成后再次調(diào)用引谜。如果您希望處理步驟并行執(zhí)行牍陌,你必須使用 Promise.all()
:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function logContent(urls) {
- await Promise.all(urls.map(
- async url => {
- const content = await httpGet(url);
- console.log(content);
- }));
- }
</pre>
map()
用于創(chuàng)建一個(gè) Promises 數(shù)組。 我們對(duì)他們的完成結(jié)果并不感興趣员咽,我們只要 await
(等待) 所有方法執(zhí)行完成。這意味著我們希望的是在 async(異步) 函數(shù)完成之后所有的執(zhí)行都已經(jīng)完成贮预。我們也可以返回 Promise.all()
贝室,但是該函數(shù)的結(jié)果是一個(gè)數(shù)組契讲,其元素都是未完成狀態(tài)的。
使用異步函數(shù)小貼士
了解你的 Promises
async(異步) 函數(shù)的基礎(chǔ)就是 Promises 對(duì)象滑频,這就是為什么理解 Promises 對(duì)于理解 async(異步) 函數(shù)至關(guān)重要捡偏。特別是當(dāng)遇到不是基于 Promises 的老代碼來(lái)實(shí)現(xiàn) async(異步) 函數(shù)時(shí),你通常別無(wú)選擇峡迷,只能用 Promise 來(lái)重構(gòu)银伟。
舉個(gè)例子,這里有個(gè) “promisified” 版本的 XMLHttpRequest
:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- function httpGet(url, responseType="") {
- return new Promise(
- function (resolve, reject) {
- const request = new XMLHttpRequest();
- request.onload = function () {
- if (this.status === 200) {
- // Success
- resolve(this.response);
- } else {
- // Something went wrong (404 etc.)
- reject(new Error(this.statusText));
- }
- };
- request.onerror = function () {
- reject(new Error(
- 'XMLHttpRequest Error: '+this.statusText));
- };
- request.open('GET', url);
- xhr.responseType = responseType;
- request.send();
- });
- }
</pre>
XMLHttpRequest 的 API 是基于回調(diào)的绘搞。通過(guò)一個(gè) async(異步) 函數(shù)來(lái)實(shí)現(xiàn)它彤避,意味著你必須在回調(diào)中返回 Promise 的完成(fulfill) 或拒絕(reject) 狀態(tài)。這是不可能的夯辖,因?yàn)槟阒荒芡ㄟ^(guò) return
或者 throw
來(lái)完成這樣的操作琉预。你不能從回調(diào)函數(shù)內(nèi)部 return
一個(gè)函數(shù)的結(jié)果。throw
也有類似的約束蒿褂。
因此圆米,異步函數(shù)的通用編碼風(fēng)格是:
- 直接使用 Promise 對(duì)象來(lái)構(gòu)建異步原語(yǔ)。
- 用異步函數(shù)來(lái)使用這些原語(yǔ)啄栓。
擴(kuò)展閱讀:“Exploring ES6” 中的 “異步編程中的 Promises 對(duì)象” 章節(jié)
立即調(diào)用異步函數(shù)表達(dá)式
有時(shí)娄帖,如果你可以在模塊或腳本的頂層使用 await ,那將是一種很好的選擇昙楚。當(dāng)然近速,它只能在異步函數(shù)中使用。您可以創(chuàng)建一個(gè)異步函數(shù) main()
并立即調(diào)用它:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function main() {
- console.log(await asyncFunction());
- }
- main();
</pre>
或者您可以使用立即調(diào)用異步函數(shù)表達(dá)式:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- (async function () {
- console.log(await asyncFunction());
- })();
</pre>
另一個(gè)選擇是立即調(diào)用異步箭頭函數(shù):
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- (async () => {
- console.log(await asyncFunction());
- })();
</pre>
用異步函數(shù)進(jìn)行單元測(cè)試
以下代碼使用 測(cè)試框架 mocha 對(duì)異步函數(shù) asyncFun1() 和 asyncFun2() 來(lái)進(jìn)行單元測(cè)試:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
import assert from 'assert';
// Bug: the following test always succeeds
test('Testing async code', function () {
asyncFunc1() // (A)
.then(result1 => {
assert.strictEqual(result1, 'a'); // (B)
return asyncFunc2();
})
.then(result2 => {
assert.strictEqual(result2, 'b'); // (C)
});
});
</pre>
然而桂肌,這個(gè)測(cè)試總是成功的数焊,因?yàn)?mocha 不會(huì)等待 B 行和 C 行斷言執(zhí)行完成。
你可以通過(guò)返回鏈?zhǔn)秸{(diào)用的 Promise 來(lái)解決這個(gè)問(wèn)題崎场,因?yàn)?mocha 會(huì)識(shí)別一個(gè)測(cè)試是否返回一個(gè) Promise 佩耳,然后等待該 Promise 完成 再進(jìn)行下一步(除非超時(shí))。
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- return asyncFunc1() // (A)
</pre>
異步函數(shù)總是返回 Promises 谭跨,這使得它們能方便的干厚、完美的來(lái)進(jìn)行這種單元測(cè)試:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- import assert from 'assert';
- test('Testing async code', async function () {
- const result1 = await asyncFunc1();
- assert.strictEqual(result1, 'a');
- const result2 = await asyncFunc2();
- assert.strictEqual(result2, 'b');
- });
</pre>
在 mocha 中使用異步單元測(cè)試異步函數(shù)有兩個(gè)優(yōu)點(diǎn):代碼更簡(jiǎn)潔,能夠準(zhǔn)確處理返回的 Promise 對(duì)象螃宙。
不要擔(dān)心沒(méi)有處理的拒絕拒態(tài)
當(dāng)前的 JavaScript 引擎可以在拒絕態(tài)未處理的情況下提出警告蛮瞄。以下代碼在過(guò)去會(huì)經(jīng)常執(zhí)行失敗,但是當(dāng)前的 JavaScript 引擎可以進(jìn)行警告:
<pre class="prettyprint lang-JavaScript linenums:1 prettyprinted" name="code" style="box-sizing: border-box; outline: 0px; padding: 0px; margin: 20px 0px; position: relative; overflow: hidden; white-space: pre-wrap; word-wrap: break-word; font-family: "courier new"; font-size: 14px; line-height: 20px; color: rgb(102, 102, 102); word-break: break-all; background: rgb(68, 68, 68); border: 1px solid rgb(39, 40, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; vertical-align: baseline; max-width: 100%;">
JavaScript 代碼:
- async function foo() {
- throw new Error('Problem!');
- }
- foo();
</pre>