@async 如何返回list_图解 Await 和 Async
原文鏈接:Await and Async Explained with Diagrams and Examples
文章目錄
簡介
JavaScript ES7中的 async/await 使得協調異步 promise 變得更容易。如果你需要從多個數據庫或 API 異步獲取數據,則可以使用 promise 和回調函數。async / await 使我們更簡潔地表達這種邏輯,并完成更易讀和可維護的代碼。
本教程將使用圖表和簡單示例來解釋 JavaScript中 的 async / await 語法。
在講解之前,我們從 promises 的簡要概述開始。如果你已經了解了 JS 中的 promise,請隨時跳過本節。
Promises
在 JavaScript 中,promise 代表非阻塞異步執行的抽象對象。JS中的 promise 與 Java 中的Future 或C#的Task類似,如果你了解它們的話那就很容易理解。
Promise 通常用于網絡和 I/O 操作,例如讀取文件或者發出 HTTP 請求。我們可以產生一個異步 promise ,并使用 then 的方法來附加一個回調函數,這個回調函數當 promise 完成時將會被觸發,這種方法不會阻止當前的“線程”執行。回調函數本身可以返回 promise,使我們可以有效地鏈接 promises。
為了容易理解,在所有示例中,我們假設 request-promise 庫已經安裝并加載為:
var rp = require('request-promise');我們做一個簡單的 HTTP GET 請求,返回一個promise:
const promise = rp('http://example.com/')現在,讓我們來看一個例子:
console.log('Starting Execution');const promise = rp('http://example.com/'); // Line 3 promise.then(result => console.log(result)); // Line 4console.log("Can't know if promise has finished yet...");我們在第3行產生了一個新的 Promise,然后在第4行新加一個回調函數。因為promise 是異步的,所以當我們到達第6行時,我們不知道 promise 是否已經完成。如果我們多次運行代碼,我們可能會每次得到不同的結果。換句話說,任何 promise 之后的代碼都是與 promise 同時運行的。
在 promise 完成之前,并沒有辦法阻止當前的操作順序。 這與 Java 中的 Future.get 不同,其允許我們阻止當前線程,然后之后完成。在 JavaScript 中,我們不能等待 promise。在 promise 之后調度代碼的唯一方法是通過 then 附加回調函數。
下圖描繪了該示例的計算過程:
promise 的計算過程。呼叫“線程”不能等待 promise 。在 promise 之后調度代碼的唯一方法是通過then方法指定回調函數。當 promise 成功返回時,只有通過then方法指定回調函數才能執行。如果它失敗了(例如由于網絡錯誤),回調函數將不會執行。為了處理失敗的 promise ,你可以通過catch附加另一個回調函數:
rp('http://example.com/').then(() => console.log('Success')).catch(e => console.log(`Failed: ${e}`))最后,為了測試一下,我們可以使用Promise.resolve和Promise.reject方法創建成功或失敗的“虛擬”promises:
const success = Promise.resolve('Resolved'); // 將會顯示 "Successful result: Resolved" success.then(result => console.log(`Successful result: ${result}`)).catch(e => console.log(`Failed with: ${e}`))const fail = Promise.reject('Err'); // 將會顯示 "Failed with: Err" fail.then(result => console.log(`Successful result: ${result}`)).catch(e => console.log(`Failed with: ${e}`))有關 promises 的更詳細的教程,請查看這篇文章
組合 Promise
使用單一 Promise 是簡單有效的。但是,當我們需要對復雜的異步邏輯進行編程時,我們可能會以組合多個 Promise。編寫所有的子句和匿名回調可能很容易失控。
例如,假設我們需要編寫一個程序:
我們首先進行第一個 HTTP 調用,并使用回調以在其完成時運行(第1-3行)。在回調中,我們為后續的 HTTP 請求產生了兩個 Promise(第8-9行)。這兩個 Promise 同時運行并且我們需要安排一個回調,當它們都完成時。因此,當它們都執行完成時,我們需要通過Promise.all(第11行)將它們組合成一個單一的 Promise。回調的結果是一個 Promise,我們需要l連接另一個回調函數來打印結果(行12-16)。
下圖描述了計算流程:
計算過程中的 Promise 組合。我們使用“Promise.all”將兩個并發的 Promise組合成一個 Promise。對于這樣一個簡單的例子,我們最終得到了 2 個回調,并且必須使用Promise.all來同步并發Promise。如果我們不得不再運行一些異步操作或添加錯誤處理怎么辦?這種方法最終很容易崩潰于then-s,Promise.all調用和回調三者混雜在一起。
Async
異步函數是用于定義返回Promise的函數的快捷方式。 例如,以下定義是等價的:
function f() {return Promise.resolve('TEST'); }// asyncF相當于f! async function asyncF() {return 'TEST'; }類似地,拋出異常的異步函數等效于返回拒絕 Promise 的函數:
function f() {return Promise.reject('Error'); }// asyncF相當于f! async function asyncF() {throw 'Error'; }Await
當我們使用 promise 之后,我們只能通過then來傳回回調函數(callback)。 不允許直接等待一個 promise 執行完畢是為了鼓勵用戶書寫非阻塞的代碼,不然用戶會更樂意寫阻塞的代碼,因為它比 promise 和回調函數更簡單。
然而,為了同步 promise, 我們需要允許 promise 之間相互等待。換句話說,如果一個異步的操作(例如封裝在一個 promise 中)就應該去等待另一個異步的操作去完成。但是 JavaScript 解釋器如何判斷一個操作是否在 promise 中運行呢?
答案就是 async 關鍵字。每一個 async 函數都會返回一個 promise。也就是說, JavaScript 解釋器就會把所有在 aysnc 函數中的操作封裝到 promise 中并異步運行。這樣就可以讓它們去等待其他的 promise 完成。
按下 await 關鍵字,await 只能在 async 函數中使用,作用是讓我們同步的等待另一個 promise 執行完畢。如果在 async 函數之外使用 promise 的話,依舊需要使用 then 回調函數:
async function f() {// 返回值將作為 promise 被處理(resolve)之后的結果const response = await rp('http://example.com/');console.log(response); } // 不能在 async 函數之外使用 await 關鍵字 // 需要使用 then 回調 f().then(() => console.log('Finished'));現在,來看看如何解決剛在在上面一節出現的問題:
// 將解決問題的方法封裝到一個異步的函數中 async function solution() {// 等待第一個 HTTP 調用并且打印出結果console.log(await rp('http://example.com/'));// 生成 HTTP 調用但是不等待它們執行完畢 - 同時運行const call2Promise = rp('http://example.com/'); // 不等待! // Line 7const call3Promise = rp('http://example.com/'); // 不等待! // Line 8//在它們都被調用之后 - 等待它們執行完畢const response2 = await call2Promise; // Line 11const response3 = await call3Promise; // Line 12console.log(response2);console.log(response3); }// 調用 async 函數 solution().then(() => console.log('Finished'))在上面的代碼段中,我們將解決方案封裝到了一個 async 函數中,這樣我們就可以直接的等待(await) promise 執行完畢。這樣避免了使用 then 回調函數。 最后,我們調用了 async 函數,這個函數只是簡單的生成了一個封裝調用其他 promise 的 promise。
在第一個例子(沒有 async 和 await)中,那些 promise 會并行啟動。這種情況下我們進行了同樣的操作(第7,8行)。注意,直到11到12行,我們都沒有使用 await。當所有的promise都執行完畢(resolve),我們才去阻塞程序的執行。之后,我們知道兩個 promise 都執行完畢了(就像在之前的例子中,使用 Promise.all(...).then(...) 一樣)。
在底層的計算過程上,這個過程和先前章節所述的過程是相同的,但是代碼更加直觀,可讀性更好。
在引擎中,async/await 實際上轉成了 promise 和 then傳入的回調函數。換句話說,它是 promise 的語法糖。每次我們使用 await,解釋器就會生成一個 promise,然后把其余的操作從 async 函數取出來放到 then 傳入的回調函數中。
考慮一下下面的例子:
async function f() {console.log('Starting F');const result = await rp('http://example.com/');console.log(result); }函數f在底層計算過程描述如下圖。由于f是異步的,它將和調用者同步運行:
函數f開始運行并且生成了一個promise。同時,函數其余的部分被封裝到回調函數中,安排在promise執行完畢之后再執行。
錯誤處理
在前面幾個例子中,我們假設promise 成功的解決(reslove).于是,等待一個promise返回結果。事實上,如果等待的promise失敗(reject)了,那么async函數將會返回一個異常(exception)。我們可以使用標準的try/catch去處理這種情況:
async function f() {try{const promiseResult = await Promise.reject('Error');} catch (e) {console.log(e);} }如果一個async函數沒有處理異常,不管它是一個被拒絕(reject)的promise還是其他的 bug 造成的,它將返回一個被拒絕(reject)的promise:
async function f() {// 拋出異常const promiseResult = await Promise.reject('Error'); }// 將打印 "Error" f().then(() => console.log('Success')).catch(err => console.log(err))async function g() {throw "Error"; }// 將打印 “Error” g().then(() => console.log('Success')).catch(err => console.log(err))使用已知的異常處理機制將使我們方便的處理被拒絕(reject)的promise。
討論
Async/await是promises的一種補充語言結構。它允許我們使用較少樣板的 promise。然而async/await不能取代純粹 promise 的需要。例如,如果從一個普通的函數或者全局范圍內調用一個async函數,我們無法使用await,我們將借助于普通的promises(譯者注:原文使用的是vanilla promise):
async function fAsync() {// 事實上返回值是 Promise.resolve(5)return 5; } // 不能調用 await fAsync(), 需要使用 then/catch fAsync().then(r => console.log(`result is ${r}`));我通常嘗試將我大部分的異步邏輯封裝到一個或者幾個 async 函數中,然后從非異步的代碼中調用。這極大地減少了我編寫then/catch回調的數量。
async/await 結構是更簡潔處理 promise 的語法糖。每一個 async/await 結構都可以使用純粹的 promise 重寫。最終,這是一個風格和簡潔方面的問題。
學者們指出并發( concurrency )和并行( parallelism )有區別。查看Rob Pike關于該主題或我之前的帖子。并發是關于組合獨立進程(在過程的一般含義中)一起工作,而并行是關于實際上同時執行多個進程。并發是關于應用程序的設計和結構,而并行性就是實際的執行。
以多線程應用程序為例。將應用程序分隔為線程定義其并發模型。這些線程在可用內核上的映射定義了其級別或并行。并發系統可以在單個處理器上有效運行,在這種情況下,它不是并行的。
在這種情況下,promise允許我們將程序分解為可并行運行的并發模塊。實際的 JavaScript執行是否并行取決于 JavaScript 解釋器實現。例如,Node.js是單線程的,如果 promise 是 CPU 綁定的,那么并不會看到很多并行進程。然而,如果您通過類似 Nashorn 的工具將代碼編譯成 java 字節碼,理論上你可以在不同的 CPU 核心上映射CPU綁定的 promise 并實現并行運行。因此,在我看來,promise(普通或通過async/await)構成了JavaScript應用程序的并發模型。
文章轉自 | Github
文章鏈接 | 圖解 Await 和 Async
總結
以上是生活随笔為你收集整理的@async 如何返回list_图解 Await 和 Async的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: graphpad prism画折线图_如
- 下一篇: Android代码设置角标,Androi