摘要:更好的語義和分別表示異步和等待,比起和更容易理解。前邊聲明關鍵字,表示內部有內部操作,調用函數會返回一個對象。等價于其中函數就是自動執行器。
async函數 定義
async函數其實就是之前說過的Generator的語法糖,用于實現異步操作。它是ES2017的新標準。
讀取兩個文件:
const fs = require("fs") const readFile = function(filename){ return new Promise(function(resolve,reject){ fs.readFile(filename,function(error,data)){ if(error) return reject(error) resolve(data) } }) } const gen = function* (){ const gen1 = yield readFile("../1.txt") const gen2 = yield readFile("../2.txt") console.log(gen1.toString()) console.log(gen2.toString()) }
如果使用async函數的話,會是這么寫
const _readFile = async function(){ const r1 = await readFile("../3.txt") const r2 = await readFile("../4.txt") console.log(r1.toString()) console.log(r2.toString()) }
一般情況下,只是把Generator的*換成async,把yield換成await。
async函數是Generator函數的改進,具體體現在四點
1.內置執行器:
相對于Generator函數需要co模塊或者next()方法來作為執行器執行,async函數自帶執行器,所以上邊的代碼只需要一句
_readFile()
可以執行。
2.更好的語義:
async和await分別表示異步和等待,比起*和yield更容易理解。
3.更廣泛的適用性:
yield后邊只能跟Thunk函數或者Promise對象,但是在async函數中,可以跟Promise對象和基本數據類型。
4.返回值是Promise
相比于Generator函數返回一個Iterator還需要遍歷,async直接返回一個Promise可以直接調用then方法和catch方法。
基本用法async函數返回一個Promise對象,可以使用then方法添加回調,然后使用await關鍵字后,會等到異步操作執行完在執行后邊的語句。
async function getPriceByName(name){ const symbol = await getSymbod(name) const price = await getPrice(symbol) return price } getPriceByName("WUBA").then(function(result){ console.log(result) })
上邊的例子,是一個通過股票的名稱,獲得股票的代碼,再獲得價錢。前邊聲明async關鍵字,表示內部有內部操作,調用函數會返回一個Promise對象。
再看下一個例子,是一個指定多少毫秒后返回一個值。
function TimeOut(time){ return new Promise(function(resolve)){ setTimeout(resolve,time) } } async asyncTimeOut = function(value,time){ const t1 = await TimeOut(time) console.log(t1) } asyncTimeOut("hello",1000) asyncTimeOut("world",2000)
async函數的多種形式
函數表達式 const fun1 = async function(){ .... } 函數聲明 async function fun2(){ .... } 箭頭函數 const fun3 = async () => { .... } 對象中的變量 let obj = { async fun4(){ .... } } obj.fun4() 類的寫法 class Async { constructor(){} async fun5(){ .... } } const a1 = new Async() a1.fun5().then()語法 返回一個Promise對象
說過很多次了,async函數返回一個Promise對象。
函數return的值將會作為then方法的參數
async function show(){ return "123" } show().then((v) => console.log(v))
show方法返回的值會被作為then方法的參數而調用。
如果在async函數內部拋出錯誤,會被catch捕獲,這個時候Promise對象變成reject狀態。
async function show2(){ throw new Error("出錯了") } show2().then( v => console.log(v), e => console.log(e) )Promise的狀態改變
async函數返回的Promise對象,必須等到函數內部所有的await執行完才會發生狀態的變化,也就是說,得等到所有await執行完,才會執行后續的then方法。
async function getText(url){ const response = await fetch(url) const text = await response.text() return text.match("../aa/[a-z]}")[1] //反正就是一個正則匹配 } const url = "...." getText(url).then(v => console.log(v))
這個例子說明,得等到兩個awiat都執行完才會console返回的數據。
await命令前邊說過,await命令后邊跟隨一個Promise對象。如果不是,會被轉成Promise對象。
async function show(){ return await "123" } show().then((v) => console.log(v))
如果await后邊的Promise對象變成了reject狀態,會被后邊的catch()捕獲。
async function fun1() { return await Promise.reject("出錯了") } fun1().catch(e => console.log(e))
如果async函數內部有多個await,但是只要一個await返回的Promise對象變成了reject狀態,則整個函數立刻捕獲異常。
如果想要前邊的正常拋出異常而不影響后邊的await語句執行,可以把前邊的寫進一個try/catch中去。
async function fun2(){ try{ await Promise.reject("出錯了") }catch(e){ } await Promise.resolev("hello") } fun2().then(v => console.log(v))使用的注意點
由于await后面跟隨的是Promise對象,所以對象可能會有兩個狀態,一個resolve一個reject。所以,最好把await代碼放到try/catch語句中比較好。
async function fun3(){ try{ await asyncFun1() await asyncFun2() } catch(e){ console.log(e) } } // 還有另外一種寫法 async function fun4(){ await asyncFun1().catch(e => console.log(e)) await asyncFun2().catch(e => console.log(e)) }
還是第一種方法更好一點。直接寫進try/catch語句中。
最好讓多個await后邊的異步操作同時發生,如果不是不存在先后順序的話。
let a1 = await get1() let a2 = await get2()
上邊的寫法,get1執行完之后才會執行get2,如果get1和get2沒有直接的關聯,那樣會很浪費時間。
//同時觸發 let [a1,a2] = await Promise.all([get1(),get2()])
如果await放到async函數之外,就會報錯,只能放到async函數內部。
async的原理原理也很簡單,就是把Generator函數和自動執行器包裝在一個函數中。
async function fn(){ ..... } // 等價于 function fn(){ return spawn(function *(){ ..... }) }
其中spawn函數就是自動執行器。
function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }實例:按照順序完成異步操作
實際開發中經常會遇到各種異步操作,這里有一個例子。一次讀取一組url,然后按照讀取的順序返回結果。
function readUrls(urls) { const textPromise = urls.map(url => { fetch(url).then(response => response.text()) }) // 按照順序讀出 textPromise.reduce((chain,textPromise) => { return chain.then(() => textPromise) .then(text => console.log(text)); },Promise.resolve()) }
分析一下,上邊的代碼,用fetch同時讀取一種url,每個fetch操作都返回一個Promise對象,放入textPromise數組,然后reduce方法一次處理每個Promise對象,然后用then連接起來,一次輸出結果。
缺點:這種方法看起來不太好理解,不太直觀,用async函數會更好一點。
async function readUrls(urls){ for(const url of urls){ const response = await fetch(url) console.log(response.text()) } }
可以看到,代碼是大大簡化了,但是會有一個新的問題,就是必須等到前邊一個讀完了,才會讀取下一個數據。
function readUrls(urls){ const textPromise = urls.map(async url => { const response = await fetch(url) return response.text() }) } //按照順序輸出 for(const text of textPromise){ console.log(await text) }
上邊的函數,雖然map方法的參數是async函數,但卻是并發執行的,因為內部是繼發執行,不影響外部。在后邊的循環中使用了await,這樣,還是會依次輸出。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89737.html
摘要:傳統的異步方法回調函數事件監聽發布訂閱之前寫過一篇關于的文章,里邊寫過關于異步的一些概念。內部函數就是的回調函數,函數首先把函數的指針指向函數的下一步方法,如果沒有,就把函數傳給函數屬性,否則直接退出。 Generator函數與異步編程 因為js是單線程語言,所以需要異步編程的存在,要不效率太低會卡死。 傳統的異步方法 回調函數 事件監聽 發布/訂閱 Promise 之前寫過一篇關...
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:在這里看尤雨溪大神的這篇小短文,非常精簡扼要地介紹了當前常用的。根據尤雨溪大神的說法,的也只是的語法糖而已。對象有三種狀態,,。對象通過和方法來規定異步結束之后的操作正確處理函數錯誤處理函數。方便進行后續的成功處理或者錯誤處理。 最近在寫一個自己的網站的時候(可以觀摩一下~Colors),在無意識中用callback寫了一段嵌套了5重回調函數的可怕的代碼。回過神來的時候被自己嚇了一跳,...
摘要:讓我們使用它從數組中返回一個值數組在中,我們可以這樣做,這是一種更簡單的方法最重要的部分是創建數組,該數組立即調用所有的我們在主函數中等待這些。所以在我們真正等待完成之前,主函數就退出了。 原文:https://pouchdb.com/2015/03/0... PouchDB最棘手的方面之一是它的API是異步的。在Stack Overflow、Github和IRC上,我看到了不少困惑的...
摘要:第二次同理,遇到了第二個函數會停下來,輸出的遍歷器對象值為,的值依然是。比如返回的遍歷器對象,都會有一個方法,這個方法掛在原型上。這三個函數共同的作用是讓函數恢復執行。 Generator的語法 generator的英文意思是生成器 簡介 關于Generator函數,我們可以理解成是一個狀態機,里面封裝了多種不同的狀態。 function* gener(){ yield hel...
閱讀 997·2023-04-25 14:20
閱讀 1875·2021-11-24 10:20
閱讀 3770·2021-11-11 16:55
閱讀 2911·2021-10-14 09:42
閱讀 3470·2019-08-30 15:56
閱讀 1156·2019-08-30 15:55
閱讀 1069·2019-08-30 15:44
閱讀 780·2019-08-29 11:28