async函式的含義和用法
async函式的是什麼?
一句話,async 函式就是 Generator 函式的語法糖。例有一個Generator依次讀取兩個檔案;
var fs = require('fs')
const readeFile = function(filename){
return new Promise(function(resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data)
})
})
}
const gen = function *(){
var f1 = yield readFile('/f1');
var f2 = yield readFile('/f2');
console.log(f1);
console.log(f2);
}
寫成async就是這樣:
const asyncFile = async function(){
var f1 = await readFile('/f1')
var f2 = await readFile('/f2')
console.log(f1);
console.log(f2);
}
async 函式的優點
- async 和 await,比起星號和 yield,語義更清楚。async 表示函式裡有非同步操作,await 表示後面的表示式需要等待結果。
- yield 命令後面只能是 Thunk 函式或 Promise 物件,而await 命令後面可以跟Promise物件也可以是物件、陣列、數字、字串這些(但是等同於同步操作)。
async函式的用法
async 函式返回一個 Promise 物件,可以使用 then 方法指定下一步操作。
當函式執行的時候,一旦遇到 await 就會先返回,等到觸發的非同步操作完成,再接著執行函式體內後面的語句。
async function getByName(name){
const name = await queryName(name)
return name
}
getByName ('qqh').then(function(res){
console.log(res)
})
上面的例子中,前面聲明瞭一個非同步函式getByName,表示該函式內部有非同步操作,呼叫函式時,會返回一個Promise物件。下面是一個指定多少毫秒輸出的例子;
async function timeout(ms){
return new Promise(function(resolve){
setTimeout(resolve, ms)
})
}
async function print(value, ms){
await timeout(ms)
console.log(value, ms)
}
print('hello word', 2000)
上面指定2秒後,輸出一個 ‘hello word, 2000’,可以看到在執行print函式的時候會先返回一個Promise,再等待await後面的timeout執行完後,再往下執行。
函式語法
async函式內部return語句返回的值,會成為then方法回撥函式的引數
async function getName(){
return 'qqh'
}
getName().then(v => console.log(v)) //qqh
await命令後面如果不是一個promise物件,會被轉成一個立即resolve的 Promise 物件
async function getName() {
return await 'qqh';
}
getName().then(v => console.log(v)) // qqh
await命令後面的 Promise 物件如果變為reject狀態,則reject的引數會被catch方法的回撥函式接收到
async function getName() {
await Promise.reject('取不到名字了');
}
getName()
.then(v => console.log(v))
.catch(e => console.log(e)) // 取不到名字了
注意事項
(1)多個await命令後面的非同步操作,如果不存在繼發關係,最好讓它們同時觸發
const name = await queryName()
const body = await queryBody()
上面的程式碼中,queryName和queryBody是兩個獨立的非同步操作,queryName完成後才會執行queryBody;
這樣比較耗時了,我們完全可以讓他們同時執行;
// 寫法一
let [name, body] = await Promise.all([queryName(), queryBody()]);
// 寫法二
let namePro = queryName();
let bodyPro = queryBody();
let name = await namePro;
let body = await bodyPro;
上面兩種寫法,queryName和queryBody都是同時觸發,這樣就可以縮短程式的執行時間。
(2)await 命令只能用在 async 函式之中,如果用在普通函式,就會報錯。
async function getList(db){
var data = [{id:1}, {id:2}, {id:3}]
//會報錯
data.forEach(v=>{
await db.post(v)
})
}
async function getList(db){
var data = [{id:1}, {id:2}, {id:3}]
//得到的結果會不正確
data.forEach(async v=>{
await db.post(v)
})
}
要解決上面程式碼的問題,我們可以採用for…of迴圈;
async function getList(db){
var data = [{id:1}, {id:2}, {id:3}]
//得到的結果會不正確
for(let v of data){
await db.post(v)
}
}
如果確實希望多個請求併發執行,可以使用 Promise.all 方法。
var db = {
post: function (v) {
return new Promise((resolve, reject)=>{
setTimeout(function () {
resolve('post success id:' + v.id)
}, 2000)
})
}
}
async function getList(db){
var data = [{id:1}, {id:2}, {id:3}]
var promises = data.map(v=>{ return db.post(v) })
var res = await Promise.all(promises)
return res
}
getList(db).then(v=>{
console.log(v)
//['post success id:1', 'post success id:2', 'post success id:3']
})