async函數,使得異步變得更方便。async函數是什麼?一句話就是 Generator的語法糖。javascript
用 Generator 依次讀取兩個文件java
var fs = require('fs')
var readFile = functiton(filename) {
return new Promise(function (resolve,reject) {
fs.readFile(fileName, function(error, data) {
if(error) return reject(error)
resolve(data)
})
})
}
var gen = function* (){
var f1 = yield readFile('/1')
var f2 = yield readFile('/2')
console.log(f1.toString())
console.log(f2.toString())
}
複製代碼
將上面寫成 async 函數promise
var asyncReadFile = async function(){
var f1 = await readFile('/1')
var f2 = await readFile('/2')
console.log(f1.toString())
console.log(f2.toString())
}
複製代碼
經過比較,async 函數就是將 Generator 函數的 * 替換成了 async ,將yield 題替換成 await。僅此而已。併發
async 函數 返回一個Promise 對象,可使用then添加回調,當函數執行的時候,一旦遇到 await 就會先返回,等到異步完成,再接着執行函數體後面的語句。異步
指定多少毫秒輸出一個值async
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value,ms) {
await timeout(ms)
cosnole.log(value)
}
asyncPrint('hello world',50)
複製代碼
寫成這種方式也能夠函數
async function timeout(ms){
await new Promise(resolve =>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value,ms){
await timeout(ms)
console.log(value)
}
複製代碼
//函數聲明
async function foo(){}
// 函數表達式
const foo = async function(){}
// 對象方法
let obj = {async foo() {}}
obj.foo.then()
// class 方法
class Storage {
constructor(){
this.catchPromise = caches.open('avatars')
}
async getAvatar(name) {
cosnt catch = await this.catchPromise;
return catch.match(`/avatars/${name}.jpg`)
}
}
const storage = new Storage()
storage.getAvatar('jack').then(...)
複製代碼
async 函數 返回一個 Promise 對象。fetch
async 內部 return 語句返回的值,會成爲 then 方法回調的參數動畫
async function f(){
return 'hello world'
}
f().then(v=>{
console.log(v) // hello world
})
複製代碼
async 函數內部拋出錯誤會致使返回的Promise對象變爲 rejected 狀態。拋出的錯誤對象 會被 catch方法回調函數接收到。ui
async 函數返回Promise對象必須等到內部全部的 await 命令後邊Promise 對象執行完纔會發生狀態改變,除非遇到 return 或者 拋出錯誤。只有 async 函數內部全部的異步操做執行完,纔會執行 then 方法指定的回調函數。
正常狀況下, await 以後應該是一個 Promise 對象。若是不是,會被轉化爲一個當即resolve 的Promise。
async function f(){
return await 123;
}
f().then(v => console.log(v)) //123
複製代碼
await 後面的Promise 變爲 rejected,reject參數會被catch到
async function f(){
await Promise.reject('error')
}
f()
.then(v => console.log(v))
.catch(e => console.log(e)) // error
複製代碼
只要有一個await語句後面的Promise變爲 reject,整個async函數都會中斷執行。 有時,咱們那但願前面一個異步操做失敗,也不要中斷後面的異步操做。這時能夠將第一個await 放在 try catch 結構裏面,這樣無論這個異步是否是成功,第二個都會執行。 或者 在await的後面Promise對象後添加一個 catch方法,處理前面可能出現的錯誤。
若是 await 後面異步操做出錯,那麼等於 async 函數返回的 Promise 對象被 reject。
防止出錯的方法也是將其放在 try...catch 代碼塊中。
async function f(){
try {
await new Promise((resolve,reject) => {
throw new Error("出錯了")
})
} catch(e){
}
return await('hello world')
}
複製代碼
若是有多個await,能夠統一放在 try catch 代碼塊中
async function f(){
try {
var val1 = await ...
var val2 = await ...
var val3 = await ...
console.log('final',val3)
} catch(e){
console.log(e)
}
}
複製代碼
使用 try catch 實現屢次重複嘗試
async function test(){
let i
for(i = 0;i<3;++i){
try{
await ...
break
} catch(err){}
}
console.log(i) // 3
}
複製代碼
第一點:await命令後面的Promise對象的結果kennel rejected,因此,最好把await放在 try catch代碼塊中。
第二點:多個 await 命令後面的異步若是不存在 繼發關係,最好讓他們同時觸發。
let foo = await getFoo()
let bar = await getBar()
複製代碼
上面代碼中,getFoo 和 getBar 是兩個獨立的異步(互不依賴)被寫成繼發關係。這樣比較耗時,由於只有getFoo完成之後纔會執行 getBar,徹底可讓他們同時觸發。
// 寫法 1
let [foo,bar] = await Promise.all([getFoo(),getBar()])
// 寫法 2
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
複製代碼
async 函數實現原理就是將Generator函數和自動執行器包裝在一個函數裏。
async function fn(){
}
// 等同於
function fn(args){
return spawn(function* (){
})
}
複製代碼
async Promise Generator進行比較
假定某個DOM元素上面,部署了一系列動畫,前一個結束,後一個纔開始。若是有一個動畫出錯,就再也不執行,返回上一個成功執行的動畫返回值。
首先是 Promise 寫法
function chainAnimationsPromise(el,animations){
// ret 保存上一個動畫返回值
var ret = null
// 新建一個Promise
var p = new Promise.resolve()
// 使用then添加全部動畫
for(var anim of animations){
p = p.then(function(val){
ret = val
return anim(elem)
})
}
return p.catch(function(e){
return ret
})
}
複製代碼
代碼徹底是Promise的API,不容易看出語義
下面是Generator寫法
function chainAnimationsGenerator(elem, animations) {
return spawn(function*(){
var ret = null
try {
for(var anim of animations){
ret = yield anim(elem)
}
} catch(e){
}
return ret
})
}
複製代碼
這個寫法的問題在於必須有一個任務運行器自動執行Generator函數,必須保證yield語句後面的表達式返回一個Promise。
最後是 async 寫法
async function chainAnimationAsync(elem,animations){
var ret = null
try {
for(var anim of animations) {
ret = await anim(elem)
}
}catch(e){
}
return rett
}
複製代碼
依次遠程讀取一組URL,按照讀取順序輸出結果
promise
function loginOrder(urls){
// 遠程讀取全部url
const textPromises = urls.map(url => {
return fetch(url).then(res => res.text())
})
//按順序輸出
textPromises.reduce((chain,textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(txt))
},Promise.resolve())
}
複製代碼
async
async function loginOrder(urls){
for(const url of urls){
const response = await fetch(url)
console.log(await response.text())
}
}
複製代碼
以上寫法是繼發的,效率很低,咱們須要同時發出遠程請求。
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內部是繼發執行,外部是不受影響的。後面的for of 內部使用了await,所以實現了按順序輸出。