ES2017新特性藤肢,async,await等

原文連接: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 代碼:
  1. async function asyncFunc() {

  2. return 123;

  3. }

  4. asyncFunc()

  5. .then(x => console.log(x));

  6. // 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 代碼:
  1. async function asyncFunc() {

  2. throw new Error('Problem!');

  3. }

  4. asyncFunc()

  5. .catch(err => console.log(err));

  6. // 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 代碼:
  1. async function asyncFunc() {

  2. const result = await otherAsyncFunc();

  3. console.log(result);

  4. }

  5. // 等價(jià)于:

  6. function asyncFunc() {

  7. return otherAsyncFunc()

  8. .then(result => {

  9. console.log(result);

  10. });

  11. }

</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 代碼:
  1. async function asyncFunc() {

  2. const result1 = await otherAsyncFunc1();

  3. console.log(result1);

  4. const result2 = await otherAsyncFunc2();

  5. console.log(result2);

  6. }

  7. // 等價(jià)于:

  8. function asyncFunc() {

  9. return otherAsyncFunc1()

  10. .then(result1 => {

  11. console.log(result1);

  12. return otherAsyncFunc2();

  13. })

  14. .then(result2 => {

  15. console.log(result2);

  16. });

  17. }

</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 代碼:
  1. async function asyncFunc() {

  2. const [result1, result2] = await Promise.all([

  3. otherAsyncFunc1(),

  4. otherAsyncFunc2(),

  5. ]);

  6. console.log(result1, result2);

  7. }

  8. // 等價(jià)于:

  9. function asyncFunc() {

  10. return Promise.all([

  11. otherAsyncFunc1(),

  12. otherAsyncFunc2(),

  13. ])

  14. .then([result1, result2] => {

  15. console.log(result1, result2);

  16. });

  17. }

</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 代碼:
  1. async function asyncFunc() {

  2. try {

  3. await otherAsyncFunc();

  4. } catch (err) {

  5. console.error(err);

  6. }

  7. }

  8. // 等價(jià)于:

  9. function asyncFunc() {

  10. return otherAsyncFunc()

  11. .catch(err => {

  12. console.error(err);

  13. });

  14. }

</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 代碼:
  1. function fetchJson(url) {
  2. return fetch(url)
  3. .then(request => request.text())
  4. .then(text => {
  5. return JSON.parse(text);
  6. })
  7. .catch(error => {
  8. console.log(ERROR: ${error.stack});
  9. });
  10. }
  11. fetchJson('http://example.com/some_file.json')
  12. .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 代碼:
  1. const fetchJson = co.wrap(function* (url) {
  2. try {
  3. let request = yield fetch(url);
  4. let text = yield request.text();
  5. return JSON.parse(text);
  6. }
  7. catch (error) {
  8. console.log(ERROR: ${error.stack});
  9. }
  10. });

</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 代碼:
  1. async function fetchJson(url) {
  2. try {
  3. let request = await fetch(url);
  4. let text = await request.text();
  5. return JSON.parse(text);
  6. }
  7. catch (error) {
  8. console.log(ERROR: ${error.stack});
  9. }
  10. }

</pre>

在內(nèi)部板辽,異步函數(shù)寫(xiě)法更類似于 generators 奇瘦。

以同步開(kāi)始,異步處理的 async(異步) 函數(shù)

以下是 async(異步)函數(shù)是如何工作的:

  1. async(異步) 函數(shù)總是返回一個(gè) Promise 對(duì)象 p 劲弦。Promise 對(duì)象在 async(異步) 函數(shù)開(kāi)始執(zhí)行時(shí)被創(chuàng)建耳标。
  2. 函數(shù)體執(zhí)行過(guò)程中,可以通過(guò) returnthrow 終止執(zhí)行邑跪〈纹拢或者通過(guò) await 暫停執(zhí)行,在這種情況下画畅,通常會(huì)在以后繼續(xù)執(zhí)行砸琅。
  3. 返回 Promise 對(duì)象 p

當(dāng)執(zhí)行 async(異步) 函數(shù)的函數(shù)體時(shí)轴踱,return x 中的 x 是 Promise 對(duì)象 p 的完成狀態(tài)的結(jié)果症脂,而 throw errp 的拒絕狀態(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 代碼:
  1. async function asyncFunc() {

  2. console.log('asyncFunc()'); // (A)

  3. return 'abc';

  4. }

  5. asyncFunc().

  6. then(x => console.log(Resolved: ${x})); // (B)

  7. console.log('main'); // (C)

  8. // Output:

  9. // asyncFunc()

  10. // main

  11. // Resolved: abc

</pre>

您可以認(rèn)為是以下的執(zhí)行順序:

  1. 行A:async(異步) 函數(shù)以同步開(kāi)始。async(異步) 函數(shù)的 Promise 通過(guò) return 來(lái)返回完成狀態(tài)的結(jié)果嘁傀。
  2. 行C:執(zhí)行繼續(xù)兴蒸。
  3. 行B:Promise 完成狀態(tài)通知是異步發(fā)生的。

返回不被包裹的 Promise 對(duì)象

Promise 的 resolve 是一項(xiàng)標(biāo)準(zhǔn)操作细办。 return 就是使用它來(lái) resolve async(異步) 函數(shù)的 Promise p 的。這意味著:

  1. 返回一個(gè)非 Promise 值蕾殴,該值將被處理成 p 的完成狀態(tài)值笑撞。
  2. 返回一個(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 代碼:
  1. async function asyncFunc() {
  2. return Promise.resolve(123);
  3. }
  4. asyncFunc()
  5. .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 代碼:
  1. async function asyncFunc() {
  2. return Promise.reject(new Error('Problem!'));
  3. }
  4. asyncFunc()
  5. .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 代碼:
  1. async function asyncFunc() {
  2. return anotherAsyncFunc();
  3. }

</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 代碼:
  1. async function asyncFunc() {
  2. return await anotherAsyncFunc();
  3. }

</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 代碼:
  1. async function asyncFunc() {
  2. const value = otherAsyncFunc(); // missing await!
  3. ···
  4. }

</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 代碼:
  1. async function foo() {
  2. await step1(); // (A)
  3. ···
  4. }

</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 代碼:
  1. async function asyncFunc() {
  2. const writer = openFile('someFile.txt');
  3. writer.write('hello'); // don’t wait
  4. writer.write('world'); // don’t wait
  5. await writer.close(); // wait for file to close
  6. }

</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 代碼:
  1. async function asyncFunc() {
  2. const writer = openFile('someFile.txt');
  3. writer.write('hello');
  4. writer.write('world');
  5. return writer.close();
  6. }

</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 代碼:
  1. async function foo() {
  2. const result1 = await asyncFunc1();
  3. const result2 = await asyncFunc2();
  4. }

</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 代碼:
  1. async function foo() {
  2. const [result1, result2] = await Promise.all([
  3. asyncFunc1(),
  4. asyncFunc2(),
  5. ]);
  6. }

</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 代碼:
  1. async function downloadContent(urls) {
  2. return urls.map(url => {
  3. // 錯(cuò)誤的語(yǔ)法!
  4. const content = await httpGet(url);
  5. return content;
  6. });
  7. }

</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 代碼:
  1. async function downloadContent(urls) {
  2. return urls.map(async (url) => { // 注意這一行中的 async ;
  3. const content = await httpGet(url);
  4. return content;
  5. });
  6. }

</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 代碼:
  1. async function downloadContent(urls) {
  2. const promiseArray = urls.map(async (url) => {
  3. const content = await httpGet(url);
  4. return content;
  5. });
  6. return await Promise.all(promiseArray);
  7. }

</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 代碼:
  1. async function downloadContent(urls) {
  2. const promiseArray = urls.map(
  3. url => httpGet(url));
  4. return await Promise.all(promiseArray);
  5. }

</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 代碼:
  1. async function downloadContent(urls) {
  2. const promiseArray = urls.map(
  3. url => httpGet(url));
  4. return Promise.all(promiseArray);
  5. }

</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 代碼:
  1. async function logContent(urls) {
  2. urls.forEach(url => {
  3. // Wrong syntax
  4. const content = await httpGet(url);
  5. console.log(content);
  6. });
  7. }

</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 代碼:
  1. async function logContent(urls) {
  2. urls.forEach(async url => {
  3. const content = await httpGet(url);
  4. console.log(content);
  5. });
  6. // Not finished here
  7. }

</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 代碼:
  1. async function logContent(urls) {
  2. for (const url of urls) {
  3. const content = await httpGet(url);
  4. console.log(content);
  5. }
  6. }

</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 代碼:
  1. async function logContent(urls) {
  2. await Promise.all(urls.map(
  3. async url => {
  4. const content = await httpGet(url);
  5. console.log(content);
  6. }));
  7. }

</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 代碼:
  1. function httpGet(url, responseType="") {
  2. return new Promise(
  3. function (resolve, reject) {
  4. const request = new XMLHttpRequest();
  5. request.onload = function () {
  6. if (this.status === 200) {
  7. // Success
  8. resolve(this.response);
  9. } else {
  10. // Something went wrong (404 etc.)
  11. reject(new Error(this.statusText));
  12. }
  13. };
  14. request.onerror = function () {
  15. reject(new Error(
  16. 'XMLHttpRequest Error: '+this.statusText));
  17. };
  18. request.open('GET', url);
  19. xhr.responseType = responseType;
  20. request.send();
  21. });
  22. }

</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 代碼:
  1. async function main() {
  2. console.log(await asyncFunction());
  3. }
  4. 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 代碼:
  1. (async function () {
  2. console.log(await asyncFunction());
  3. })();

</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 代碼:
  1. (async () => {
  2. console.log(await asyncFunction());
  3. })();

</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 代碼:
  1. import assert from 'assert';

  2. // Bug: the following test always succeeds

  3. test('Testing async code', function () {

  4. asyncFunc1() // (A)

  5. .then(result1 => {

  6. assert.strictEqual(result1, 'a'); // (B)

  7. return asyncFunc2();

  8. })

  9. .then(result2 => {

  10. assert.strictEqual(result2, 'b'); // (C)

  11. });

  12. });

</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 代碼:
  1. 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 代碼:
  1. import assert from 'assert';
  2. test('Testing async code', async function () {
  3. const result1 = await asyncFunc1();
  4. assert.strictEqual(result1, 'a');
  5. const result2 = await asyncFunc2();
  6. assert.strictEqual(result2, 'b');
  7. });

</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 代碼:
  1. async function foo() {
  2. throw new Error('Problem!');
  3. }
  4. foo();

</pre>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谆扎,一起剝皮案震驚了整個(gè)濱河市挂捅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堂湖,老刑警劉巖闲先,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件状土,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡伺糠,警方通過(guò)查閱死者的電腦和手機(jī)蒙谓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)训桶,“玉大人累驮,你說(shuō)我怎么就攤上這事《娼遥” “怎么了谤专?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)琉朽。 經(jīng)常有香客問(wèn)我毒租,道長(zhǎng),這世上最難降的妖魔是什么箱叁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任墅垮,我火速辦了婚禮,結(jié)果婚禮上耕漱,老公的妹妹穿的比我還像新娘算色。我一直安慰自己,他們只是感情好螟够,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布灾梦。 她就那樣靜靜地躺著,像睡著了一般妓笙。 火紅的嫁衣襯著肌膚如雪若河。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天寞宫,我揣著相機(jī)與錄音萧福,去河邊找鬼。 笑死辈赋,一個(gè)胖子當(dāng)著我的面吹牛鲫忍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钥屈,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼悟民,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了篷就?” 一聲冷哼從身側(cè)響起射亏,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鸦泳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體银锻,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡永品,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年做鹰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鼎姐。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钾麸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炕桨,到底是詐尸還是另有隱情饭尝,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布献宫,位于F島的核電站钥平,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏姊途。R本人自食惡果不足惜涉瘾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捷兰。 院中可真熱鬧立叛,春花似錦、人聲如沸贡茅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顶考。三九已至赁还,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驹沿,已是汗流浹背艘策。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甚负,地道東北人柬焕。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梭域,于是被迫代替她去往敵國(guó)和親斑举。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 月集村病涨、柳溝村富玷、劉莊村,三個(gè)村莊幾乎呈等邊三角形,分布在與山東交界的一片廣袤的土地上赎懦,這里地廣人稀雀鹃,人均土地二畝多...
    始行者閱讀 157評(píng)論 0 0
  • 踐行第10天2017.29 城市:宜興 姓名:糖糖 一、[設(shè)定目標(biāo)(3件幫助人的好事)]: 1.轉(zhuǎn)發(fā)正能量善知識(shí)鏈...
    愛(ài)陽(yáng)光的TT閱讀 219評(píng)論 0 0