摘要:在中,表示抽象的非阻塞異步執(zhí)行。在完成之后安排代碼的唯一方式是通過方法綁定回調(diào)函數(shù)。下圖描述了該示例的計(jì)算過程方法中綁定的回調(diào)函數(shù)只有當(dāng)成功的時(shí)候才會(huì)調(diào)用。為了處理失敗的,需要通過綁定另一個(gè)回調(diào)函數(shù)。
介紹
ES7中,async/await 語(yǔ)法使異步promise的協(xié)調(diào)變得很簡(jiǎn)單。如果你需要以特定順序異步獲取來(lái)自多個(gè)數(shù)據(jù)庫(kù)或API的數(shù)據(jù),可以使用雜亂的promise或回調(diào)函數(shù)。async/await使我們可以更簡(jiǎn)便地處理這種邏輯,代碼的可讀性和可維護(hù)性也更好。
在該教程中,我們用圖表和一些簡(jiǎn)單的例子來(lái)解釋async/await的語(yǔ)法和語(yǔ)義。
開始講解之前,我們先對(duì)promise進(jìn)行一個(gè)簡(jiǎn)單的概述,如果你對(duì)promise已經(jīng)很熟悉了,可以跳過該部分內(nèi)容。
在js中,promise表示抽象的非阻塞異步執(zhí)行。js中的promise與Java中的 Future或C#中的Task很相似。
promise通常用于網(wǎng)絡(luò)和I/O操作-例如,讀取文件,發(fā)起HTTP請(qǐng)求。為了不阻塞當(dāng)前執(zhí)行線程,我們創(chuàng)建一個(gè)異步promise,使用then方法綁定一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)會(huì)在promise完成后觸發(fā)。回調(diào)函數(shù)本身也可以返回一個(gè)promise,所以promise可以高效的鏈?zhǔn)秸{(diào)用。
簡(jiǎn)單起見,所有的例子中我們都假定request-promise庫(kù)已經(jīng)安裝和加載完成了,如下所示:
var rp = require("request-promise");
現(xiàn)在我們可以像這樣發(fā)起一個(gè)簡(jiǎn)單的HTTP GET請(qǐng)求,該方法返回一個(gè)promise:
const promise = rp("http://example.com/")
接下來(lái),看一個(gè)例子:
console.log("Starting Execution"); const promise = rp("http://example.com/"); promise.then(result => console.log(result)); console.log("Can"t know if promise has finished yet...");
在第三行,我們創(chuàng)建了一個(gè)promise,然后我們?cè)诘谒男兄袨槠浣壎艘粋€(gè)回調(diào)函數(shù)。由于promise是異步執(zhí)行的
,所以執(zhí)行到第六行時(shí),我們不確定promise有沒有完成。多次運(yùn)行上面的代碼,得到的結(jié)果可能每次都不一樣。更通俗地講,promise后面的代碼和promise是并行運(yùn)行的。
在promise完成之前,沒有辦法中斷當(dāng)前的操作序列。這與Java中的 Future.get是不同的,Future.get允許我們中斷當(dāng)前的線程直到Future完成。js中,我們不會(huì)輕易地等待promise執(zhí)行完成。在promise完成之后安排代碼的唯一方式是通過then方法綁定回調(diào)函數(shù)。
下圖描述了該示例的計(jì)算過程:
then方法中綁定的回調(diào)函數(shù)只有當(dāng)promise成功的時(shí)候才會(huì)調(diào)用。如果promise失敗的話(例如,由于網(wǎng)絡(luò)錯(cuò)誤),回調(diào)不會(huì)執(zhí)行。為了處理失敗的promise,需要通過catch綁定另一個(gè)回調(diào)函數(shù)。
rp("http://example.com/"). then(() => console.log("Success")). catch(e => console.log(`Failed: ${e}`))
最后,為了測(cè)試一下效果,我們通過Promise.resolve和Promise.reject簡(jiǎn)單地生成成功和失敗的promise:
const success = Promise.resolve("Resolved"); // Will print "Successful result: Resolved" success. then(result => console.log(`Successful result: ${result}`)). catch(e => console.log(`Failed with: ${e}`)) const fail = Promise.reject("Err"); // Will print "Failed with: Err" fail. then(result => console.log(`Successful result: ${result}`)). catch(e => console.log(`Failed with: ${e}`))
有關(guān)promise更詳細(xì)的教程,查看這篇文章
問題-組合Promise單個(gè)promise是很簡(jiǎn)單的??墒牵覀兙帉憦?fù)雜的異步邏輯時(shí),可能需要組合使用多個(gè)promise來(lái)處理。大量的then語(yǔ)句和匿名回調(diào)函數(shù)很容易讓代碼變得不可維護(hù)。
例如,我們要編寫一個(gè)如下功能的代碼:
發(fā)起一個(gè)HTTP請(qǐng)求,等待完成后,打印出結(jié)果
然后發(fā)起兩個(gè)并行的HTTP請(qǐng)求;
后兩個(gè)請(qǐng)求都完成后,打印出他們的結(jié)果。
下面的代碼片段演示了上述功能的實(shí)現(xiàn):
// Make the first call const call1Promise = rp("http://example.com/"); call1Promise.then(result1 => { // Executes after the first request has finished console.log(result1); const call2Promise = rp("http://example.com/"); const call3Promise = rp("http://example.com/"); return Promise.all([call2Promise, call3Promise]); }).then(arr => { // Executes after both promises have finished console.log(arr[0]); console.log(arr[1]); })
首先發(fā)起第一個(gè)HTTP請(qǐng)求,當(dāng)該請(qǐng)求完成后,調(diào)用它的回調(diào)函數(shù)(1-3行)。在回調(diào)函數(shù)中,我們又相繼發(fā)起兩個(gè)HTTP請(qǐng)求生成了兩個(gè)promise。這兩個(gè)promise并行運(yùn)行;當(dāng)他們都執(zhí)行完后,我們還需要為其綁定一個(gè)回調(diào)函數(shù)。因此,我們用promise.all將這兩個(gè)promise組合成一個(gè)promise, 只有當(dāng)他們都完成后,這個(gè)promise才會(huì)完成。由于第一個(gè)回調(diào)函數(shù)的結(jié)果是promise,因此我們鏈?zhǔn)降卣{(diào)用另一個(gè)then方法和回調(diào)函數(shù)輸出最終結(jié)果。
下圖描述了這個(gè)執(zhí)行過程:
對(duì)于這么簡(jiǎn)單的例子,我們就用了兩個(gè)then回調(diào)和promise.all來(lái)同步并行的promise。試想如果我們執(zhí)行更多的異步操作或者增加錯(cuò)誤處理函數(shù)呢?這種方式很容易讓代碼變成一堆雜亂的then、promise.all和回調(diào)函數(shù)。
Async 函數(shù)async 函數(shù)提供了一種簡(jiǎn)潔的方式來(lái)定義一個(gè)返回promise的函數(shù)。
例如,下面兩種定義是等價(jià)的:
function f() { return Promise.resolve("TEST"); } // asyncF is equivalent to f! async function asyncF() { return "TEST"; }
相似地,在異步函數(shù)拋出異常與返回一個(gè)reject promise對(duì)象的函數(shù)等價(jià):
function f() { return Promise.reject("Error"); } // asyncF is equivalent to f! async function asyncF() { throw "Error"; }Await
我們不能同步等待promise的完成。只能通過then方法傳入一個(gè)回調(diào)函數(shù)。我們鼓勵(lì)非阻塞編程,因此同步等待promise是不允許的。否則,開發(fā)者會(huì)產(chǎn)生編寫同步腳本的想法,畢竟同步編程要簡(jiǎn)單的多。
但是,為了同步promise我們需要允許他們等待彼此的完成。換句話說(shuō),如果操作是異步的(也就是說(shuō)包裹在promise中),它應(yīng)該可以等待其他異步操作的完成。但是,js解析器怎么知道操作是否跑在promise中?
答案是async關(guān)鍵字。每個(gè)async函數(shù)返回一個(gè)promise。因此,js解析器知道所有的操作都位于async函數(shù)中,并將所有的代碼包裹在promise中異步地執(zhí)行。所以,async函數(shù),允許操作等待其他promise的完成。
說(shuō)一下await關(guān)鍵字。它只能用在async函數(shù)中,允許我們同步等待promise的完成。如果在async函數(shù)外邊使用promise,我們?nèi)匀恍枰褂?b>then回調(diào)函數(shù)。
async function f(){ // response will evaluate as the resolved value of the promise const response = await rp("http://example.com/"); console.log(response); } // We can"t use await outside of async function. // We need to use then callbacks .... f().then(() => console.log("Finished"));
現(xiàn)在我們看一下前面的那個(gè)例子如何用async/await進(jìn)行改寫:
/ Encapsulate the solution in an async function async function solution() { // Wait for the first HTTP call and print the result console.log(await rp("http://example.com/")); // Spawn the HTTP calls without waiting for them - run them concurrently const call2Promise = rp("http://example.com/"); // Does not wait! const call3Promise = rp("http://example.com/"); // Does not wait! // After they are both spawn - wait for both of them const response2 = await call2Promise; const response3 = await call3Promise; console.log(response2); console.log(response3); } // Call the async function solution().then(() => console.log("Finished"));
以上代碼,我們的解決方案就封裝在了async函數(shù)中。我們可以直接await promise的執(zhí)行,省掉了then回調(diào)函數(shù)。最后,我們只需要調(diào)用async函數(shù)。它封裝了調(diào)用其他promise的邏輯,并返回一個(gè)promise。
實(shí)際上在上面的例子中,promise是并行觸發(fā)的。本例中也一樣(7-8行)。注意第12-13行我們使用了await阻塞主線程,等待所有的promise執(zhí)行完成。后面,我們看到promise都完成了,和前面的例子類似(promise.all(...).then(...))。
其執(zhí)行流程與前例的流程是相等的。但是,代碼變得更具可讀性和簡(jiǎn)潔。
底層實(shí)現(xiàn)上,await/async實(shí)際上轉(zhuǎn)換成了promise,換句話說(shuō),await/async是promise的語(yǔ)法糖。每次我們使用await時(shí),js解析器會(huì)生成一個(gè)promise,并將async函數(shù)中的剩余代碼放到then回調(diào)中去執(zhí)行。
思考下面的例子:
async function f() { console.log("Starting F"); const result = await rp("http://example.com/"); console.log(result); }
下面描述函數(shù)f的基本計(jì)算過程。由于f是異步的,它會(huì)與調(diào)用方并行執(zhí)行:
函數(shù)f開始執(zhí)行,遇到await后生成一個(gè)promise。此時(shí),函數(shù)的其余部分被封裝在回調(diào)中,并在promise完成后執(zhí)行。
前面的大部分例子中,我們都是假設(shè)promise成功完成了。因此,等待promise返回一個(gè)值。如果我們等待的promise失敗了,在async函數(shù)中會(huì)導(dǎo)致一個(gè)異常。我們可以使用標(biāo)準(zhǔn)的try/catch來(lái)捕獲和處理它。
async function f() { try { const promiseResult = await Promise.reject("Error"); } catch (e){ console.log(e); } }
如果async函數(shù)沒有處理異常,不管是promise reject了,還是產(chǎn)生了其他bug,它都會(huì)返回一個(gè)rejected的promise對(duì)象。
async function f() { // Throws an exception const promiseResult = await Promise.reject("Error"); } // Will print "Error" f(). then(() => console.log("Success")). catch(err => console.log(err)) async function g() { throw "Error"; } // Will print "Error" g(). then(() => console.log("Success")). catch(err => console.log(err))
這給我們提供了一種簡(jiǎn)便的方法,通過已知的異常處理機(jī)制來(lái)處理被rejected的promise。
討論async/await 在語(yǔ)言結(jié)構(gòu)上是對(duì)promise的補(bǔ)充。但是,async/await 并不能取代純promise的需求。例如,在正常函數(shù)和全局作用域我們不能使用await,所以需要使用普通的promise:
async function fAsync() { // actual return value is Promise.resolve(5) return 5; } // can"t call "await fAsync()". Need to use then/catch fAsync().then(r => console.log(`result is ${r}`));
我通常會(huì)將異步邏輯封裝到一個(gè)或者少數(shù)幾個(gè)async函數(shù)中,然后在非異步代碼中調(diào)用async函數(shù)。這樣我可以最小化降低書寫then/catch的數(shù)量。
學(xué)者們指出,并發(fā)性和并行性是有區(qū)別的。并發(fā)性是指將獨(dú)立的進(jìn)程(一般意義上的進(jìn)程)組合在一起,而并行實(shí)際上是同時(shí)執(zhí)行多個(gè)進(jìn)程。并發(fā)性是關(guān)于應(yīng)用程序設(shè)計(jì)和結(jié)構(gòu)的,而并行性是關(guān)于實(shí)際執(zhí)行的。
我們以一個(gè)多線程應(yīng)用程序?yàn)槔?。?yīng)用程序分離到線程定義了它的并發(fā)模型。這些線程在可用內(nèi)核上的映射定義了它的級(jí)別或并行性。并發(fā)系統(tǒng)可以在單個(gè)處理器上高效運(yùn)行,在這種情況下,它不是并行的。
就此而言,promise允許我們將一個(gè)程序分解為并行的并發(fā)模塊,也可以不并行運(yùn)行。實(shí)際的JavaScript執(zhí)行是否并行取決于實(shí)現(xiàn)。例如,Node Js是單線程的,如果一個(gè)promise是CPU綁定的,你就不會(huì)看到太多的并行性。然而,如果你通過像Nashorn這樣的東西把你的代碼編譯成java字節(jié)碼,理論上你可能能夠在不同核心上映射CPU綁定的promise,并且實(shí)現(xiàn)并行性。因此,在我看來(lái),promise(無(wú)論是普通的或通過await/async)構(gòu)成了JavaScript應(yīng)用程序的并發(fā)模型。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/89634.html
摘要:編寫組件時(shí)要考慮的基本準(zhǔn)則是單一職責(zé)原則。這些更改通常要求組件在隔離狀態(tài)下易于修改這也是的目標(biāo)。解決多重責(zé)任問題需要將分割為兩個(gè)組件和。組件之間的通信是通過實(shí)現(xiàn)。更改的唯一原因是修改表單字段。 翻譯:劉小夕原文鏈接:https://dmitripavlutin.com/7-... 原文的篇幅非常長(zhǎng),不過內(nèi)容太過于吸引我,還是忍不住要翻譯出來(lái)。此篇文章對(duì)編寫可重用和可維護(hù)的React組...
摘要:原文地址原文作者翻譯作者是在版本中引入的,它對(duì)于中的異步編程而言是一個(gè)巨大的提升??赡軙?huì)產(chǎn)生誤導(dǎo)一些文章把和進(jìn)行了比較,同時(shí)說(shuō)它是異步編程演變過程中的下一代解決方案,對(duì)此我不敢茍同。結(jié)論在中引入的關(guān)鍵字無(wú)疑是對(duì)異步編程的一大加強(qiáng)。 原文地址: https://hackernoon.com/javasc...原文作者: Charlee Li 翻譯作者: Xixi20160512 asy...
摘要:即為裝飾器函數(shù)的這里主要為了獲取路由路徑的前綴,為請(qǐng)求方法,為請(qǐng)求路徑,為請(qǐng)求執(zhí)行的函數(shù)。下邊是設(shè)置路由路徑前綴和塞入內(nèi)容的裝飾器函數(shù)就不多說(shuō)了,就是掛載前綴路徑到類的原型對(duì)象上,這里需要注意的是作用于類,所以是被修飾的類本身。 很多面對(duì)象語(yǔ)言中都有裝飾器(Decorator)函數(shù)的概念,Javascript語(yǔ)言的ES7標(biāo)準(zhǔn)中也提及了Decorator,個(gè)人認(rèn)為裝飾器是和async/a...
摘要:想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來(lái)篇優(yōu)質(zhì)文章等著你引入的在的異步編程中是一個(gè)極好的改進(jìn)。可能會(huì)產(chǎn)生誤導(dǎo)一些文章將與進(jìn)行了比較,并聲稱它是下一代異步編程風(fēng)格,對(duì)此作者深表異議。結(jié)論引入的關(guān)鍵字無(wú)疑是對(duì)異步編程的改進(jìn)。 showImg(https://segmentfault.com/img/bVbjFP0?w=800&h=450); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇...
閱讀 3589·2021-11-17 17:01
閱讀 3998·2021-11-08 13:12
閱讀 2558·2021-10-08 10:04
閱讀 782·2021-09-29 09:35
閱讀 1490·2021-09-26 10:12
閱讀 2203·2021-09-07 09:58
閱讀 2048·2019-08-30 15:55
閱讀 2201·2019-08-30 13:14