async是什麼?它和Genernator函數外觀上的區別是什麼?javascript
const asyncReadFile = async function(){ const f1 = await readFile(url1) const f2 = await readFile(url2) }
async是Genernator函數的語法糖。java
async
和Genernator
的區別有兩點:promise
Genernator
的 *
變成async
yield
變成 await
async
對Genernator
作了哪些改進?併發
執行時和普通函數同樣,只須要調用函數就完了,不須要 next()
方法異步
async
表示它後面的函數裏有異步操做;await
表示緊跟在後面的表達式須要等待結果async
await後面能夠Genernator
, Promise對象
和原始類型的值。(原始類型的值會被自動轉化爲resolved狀態的Promise對象)函數
async
函數返回的是Promise
對象,方便用 then
進行下一步操做fetch
async
函數可當作多個異步操做,包裝成一個Promise
對象,而await
命令就是內部then
命令的語法糖。async
執行的例子:this
function timeout(ms){ return new Promise((resolve) => { setTimeout(resolve, ms) }) } async function asyncPrint(value, ms){ await timeout(ms); console.log(value) } asyncPrint("你好啊", 5000)
5s以後打印 「你好啊」 url
以上 asyncPrint
的意思是,等待timeout函數執行完了以後,纔會繼續執行 console.log(value)
async函數的幾種使用場景?
五種場景下:
函數聲明
async function foo(){}
函數表達式
const foo = async function(){}
箭頭函數
const foo = async () => {}
對象的方法
let obj = { async foo(){ } } obj.foo().then(...)
class方法
class Storage{ constructor(){ this.cachePromise = caches.open("avatars") } async getAvatar(name){ const cache = await this.cachePromise; return cache.match() } } const storage = new Storage() storage.getAvatar("joy").then(...)
返回一個 Promise
對象。
async
函數內部 return語句的返回值,會成爲then
方法回調函數的參數。
async
函數內部拋出的錯誤,會致使返回的Promise
對象變爲 rejected
狀態
`async
函數中Promise
對象是如何變化的?async
函數返回的是Promise
對象P。必須等到內部 await
命令的Promise
對象執行完後,P纔會發生狀態改變,除非遇到return語句,或者拋出了錯誤。
換言之,async
函數 內部的異步操做執行完了,纔會執行調用它時後面then
方法指定的回調函數。
await
命令的返回值是什麼?
await
後面是Promise
對象,會返回此對象的結果;若是不是Promise
對象,直接返回對應的值await
命令後面跟了 thenable
對象,會把 thenable
對象當作 Promise
對象來處理await
後面的Promise
對象若是變爲 rejected
會怎樣?
rejected的參數會被async
函數catch方法的回調函數接收到。
async function f() { await Promise.reject('出錯了'); } f() .then(v => console.log(v)) .catch(e => console.log(e))
任何一個await
語句的Promise
對象變爲reject狀態,整個async
函數都會被中斷
async function f() { await Promise.reject('出錯了'); await Promise.resolve('hello world'); // 不會執行 }
若是但願前一個異步操做失敗,不中斷後面的異步操做,怎麼處理?
兩種方法:
把第一個await
放到 try...catch
裏面
async function f(){ try{ await Promise.reject("出錯了") }catch(e){} return await Promise.resolve("hello world") } f().then(v =>console.log(v)).catch(e => console.log(e)) // 打印的是 hello world
await
後面的Promise
對象再跟一個 catch
方法,處理前面可能出現的錯誤
async function f(){ await Promise.reject("出錯啦").catch(e => { console.log("await 內部promise被reject了") }) return await Promise.resolve("hello world"); } f().then(v => console.log(v)).catch(e => console.log(e))
打印的內容是:
await 內部promise被reject了
hello world
await後面的異步操做出錯了(例如某行代碼throw 了一個Error),是什麼意思?
async
函數返回的Promise
對象被reject
了
async function f() { await new Promise(function (resolve, reject) { throw new Error('出錯了'); }); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // catch執行了, e就是拋出的錯誤對象 new Error('出錯了')
如何防止出錯呢?
仍是將其放到try{ }catch(e){ }
代碼塊中。
若是有多個await
命令,能夠將其統一放到try{ }catch(e){ }
結構裏。
下面是實際例子: 屢次發起客戶端請求,若是請求成功,跳出循環往下執行;若是不成功,繼續請求,直到達到最大數目 NUM_RETRIES
async function test(){ let i; for(i = 0; i < NUM_RETRIES; ++i){ try{ await superagent.get(url) break; }catch(err){} } console.log(i) } test()
若是await
操做成功,則會break,跳出for循環;若是await
操做不成功,則會被catch住,而後繼續下一輪for循環,直到超過 NUM_RETRIES或者 await
操做成功。
async
和await
有哪些使用上注意的點?
await
命令後的Promise
對象可能reject
,所以await
命令最好放在try{ }catch(e){ }
代碼塊中
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } // 另外一種寫法 async function myFunction() { await somethingThatReturnsAPromise() .catch(function (err) { console.log(err); }); }
多個await
異步操做時,若是不存在繼發關係,讓它們同時觸發比較好
能夠結合 Promise.all
方法
// 寫法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 寫法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
await
命令只能用在async
函數中,用在普通函數裏會報錯注意forEach
的回調函數,await
也不能出如今回調函數裏
async
函數能夠保留運行堆棧
看例子:
const a = () => { b().then(() => c()) ; } const a = async () => { await b(); c(); }
上面的例子中,b運行時,a可能已經執行完了。若是此時b或c報錯,錯誤堆棧將不包括a
下面例子中,b運行時,a只是暫停,若此時b或者c報錯了,錯誤堆棧中將包括a
與Promise
寫法和Genernator
寫法,async
有什麼好處?
Promise
寫法有不少catch
和then
,語義性不強。
Genernator
函數須要有一個任務運行器,自動執行Genernator
函數,而且 yield
後面的表達式,必須返回Promise
對象
async
最簡潔,最符合語義,將Genernator
寫法的自動執行器,改在 語言層面提供,不暴露給用戶,代碼量最少。
async function chainAnimationAsync(elem, ainmations){ let ret = null try{ for(letanim of animations){ ret = await anim(elem); } }catch(e){} return ret }
async
的實例: 按順序完成異步操做——依次遠程讀取一組URL,而後按照讀取順序輸出結果
async function logInOrder(urls){ const textPromises = urls.map(async url => { const response = await fetch(url) return response.text() }) for(const textPromise of textPromises){ console.log(await textPromise) } }
map的參數是async
函數。這幾個async
是併發的。只有async
函數內部纔是繼發的【const response = await fetch(url)
比 return response.text()
先執行】,外部並不受影響。
後面在 for...of
循環內部使用了 await
,這幾個await
是順序執行。