1. 程式人生 > >async函式的含義和用法

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 函式的優點

  1. async 和 await,比起星號和 yield,語義更清楚。async 表示函式裡有非同步操作,await 表示後面的表示式需要等待結果。
  2. 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']
})