JavaScript中的async/await詳解

1、前言

​ async函數,也就是我們常說的async/await,是在ES2017(ES8)引入的新特性,主要目的是為了簡化使用基於Promise的API時所需的語法。asyncawait關鍵字讓我們可以用一種更簡潔的方式寫出基於Promise的異步行為,而無需刻意地鏈式調用Promise。

2、詳解

​ async表示函數里有異步操作,await表示緊跟在後面的表達式需要等待結果。需要注意的是await關鍵字只在async函數內有效,如果在async函數體之外使用它,會拋出語法錯誤。

2.1、async

​ async函數返回一個 Promise對象,可以使用then方法添加回調函數。只要使用async,不管函數內部返回的是不是Promise對象,都會被包裝成Promise對象。

話不多說,上代碼看效果:

2.1.1、函數返回非Promise對象

async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);

​ 可以看出函數直接返回字符串時,返回的是Promise對象,相當於直接通過Promise.resolve()將字符串封裝為Promise對象。如果函數沒有返回值時,PromiseResult結果為undefined。

2.1.2、函數返回Promise對象

	async function testAsync() {
		return new Promise(function(resolve, reject) {
			if (true) {
				resolve('resolve return')
			} else {
				reject('reject return')
			}
		})
	}
	console.log(testAsync());

可以看出返回的也是Promise對象。

2.2、await

​ await關鍵字可以跟在任意變量或者表達式之前,但通常await後面會跟一個異步過程。await使用時,會阻塞後續代碼執行。我們先拋開async,單獨談await。

​ 由於await只能在async標識的函數內使用,以下例子請在瀏覽器控制台執行看效果。

function testAsync() {
	return new Promise(function(resolve, reject) {
		setTimeout(function() {
			if (true) {
				console.log('請求中...')
				resolve('resolve return')
			} else {
				reject('reject return')
			}
		}, 2000)
	})
}
var result = await testAsync();
var result1 = await "testAsync後執行";
console.log(result);
console.log(result1);

​ 可以看出,使用了await後,必須得等testAsync方法執行完後,才會執行後續代碼。您也可以嘗試一下把testAsync前的async去掉,看看跟加上await時有啥區別。

2.3、async、await結合使用

​ 上面我們知道了await會阻塞後續代碼運行,那怎麼解決這個問題呢?就需要用到async,使用async後,函數執行時,一旦遇到await就會先返回一個Promise對象,等到await後的操作完成後,再接着執行async函數體內的語句。

​ 先上語法:

 async function 函數名() {
 	await XXX;
 }

​ 上示例代碼:

function testAsync() {
	return new Promise(function(resolve, reject) {
		setTimeout(function() {
			if (true) {
				console.log('請求中...')
				resolve('resolve return')
			} else {
				reject('reject return')
			}
		}, 2000)
	})
}

function testAsync2() {
	return new Promise(function(resolve, reject) {
		setTimeout(function() {
			if (true) {
				console.log('請求中2...')
				resolve('resolve return2')
			} else {
				reject('reject return2')
			}
		}, 2000)
	})
}

async function test() {
	console.log('test開始...');
	var value1 = await testAsync();
	console.log(value1);
	var value2 = await testAsync2();
	console.log(value2);
	var value3 = await 'test結束...';
	console.log(value3);
}

console.log(test());

​ 上圖可以看出遇到第一個await後,立即返回了Promise對象,然後再按順序去執行testAsync函數,等待testAsync函數執行後再去執行testAsync2函數。還可以看出async函數可以簡化Promise的語法,以往我們需要使用.then去處理回調,現在我們可以使用await像寫同步代碼一樣去寫異步代碼。

​ 我們再升級一下,在上面的基礎上再加入兩個普通函數:

function fun1() {
	return '函數1'

}

function fun2() {
	return '函數2'
}

function fun3() {
	console.log(fun1());
	console.log(test()); // async/await函數
	console.log(fun2());
}

 console.log(fun3());

我們先梳理一下函數的執行過程,

1、先執行函數1

2、進入test函數並輸出開始

3、在test函數中遇到await,立即返回Promise對象

4、執行函數2

5、執行test函數中的testAsync方法

6、等到test函數中的testAsync方法執行完後,繼續執行testAsync2方法

7、test函數結束

​ 可以看出,async函數在遇到await後會立即返回Promise對象,繼續執行async函數外部後續邏輯,async函數內部會被await阻塞並按順序執行代碼邏輯。

2.4、async、await異常處理

​ await後面的函數是有可能出現異常的,所以最好把await命令放在try…catch代碼塊中。如果await後是Promise對象,也可以使用.catch進行捕獲。

 // 第一種寫法
 async function myFunction() {
   try {
     await something();
   } catch (err) {
     console.log(err);
   }
 }
 
 // 第二種寫法
 async function myFunction() {
   await somethingPromise()
   .catch(function (err) {
     console.log(err);
   });
 }

3、總結

​ async函數在遇到await後會立即返回Promise對象,繼續執行async函數外部邏輯,async函數內部會被await阻塞並按順序執行代碼邏輯。

​ 可以使用try…catch或.catch對async函數進行異常處理。