第二十一節:async異步函數和await關鍵字詳解、異常處理方案


一. async異步函數

1. 什么是異步函數?

(1).async關鍵字用於聲明一個異步函數.

     async是asynchronous單詞的縮寫,異步、非同步

     sync是synchronous單詞的縮寫,同步、同時

(2).async異步函數有很多種寫法

{
	console.log("---------- 1. 異步函數的幾種寫法------------");
	async function foo1() {}
	const foo2 = async function () {};
	const foo3 = async () => {};
	class Person {
		async foo() {}
	} 
}

2. 異步函數的執行流程

   異步函數的內部代碼執行過程和普通的函數是一致的(在不使用await關鍵字的情況下),默認情況下也是會被同步執行。

代碼分享:

{
	console.log("----------2. 異步函數的執行流程------------");
	async function foo() {
		console.log("foo function start~");

		console.log("內部的代碼執行1");
		console.log("內部的代碼執行2");
		console.log("內部的代碼執行3");

		console.log("foo function end~");
	}
	console.log("script start");
	foo();
	console.log("script end"); 
	// 運行結果如下:和普通函數一樣
	/*      
            script start
            foo function start~
            內部的代碼執行1
            內部的代碼執行2
            內部的代碼執行3
            foo function end~
            script end 
    */
}

3. 異步函數的返回值

  異步函數的返回值和普通函數是有區別的, 異步函數的返回值一定是一個Promise,但分下面幾種情況:

(1). 異步函數的返回值如果是普通值或對象,該異步函數的返回值回被包裹在Promise.resolve中

注:如果不寫返回值,則 return undefined;

{
	console.log("--------3.1 返回一個普通的值或者對象----------");
	async function foo() {
		console.log("foo function start~");
		console.log("內部代碼執行");
		console.log("foo function end~");
		return "okok";
	}
	const promiseResult = foo();
	promiseResult.then(res => console.log("result:" + res)); //result:okok 
}

不寫返回值代碼:

{
	console.log("--------3.1 不寫返回值----------");
	async function foo() {
		console.log("foo function start~");
		console.log("內部代碼執行");
		console.log("foo function end~");
	}
	const promiseResult = foo();
	promiseResult.then(res => console.log("result:" + res)); //result:undefined 
}

 (2). 異步函數的返回值是Promise,則狀態由Promise內部的是resolve 還是 reject 來決定;

{
	console.log("-------- 3.2 返回一個Promise----------");
	async function foo() {
		console.log("foo function start~");
		console.log("內部代碼執行");
		console.log("foo function end~");
		return new Promise((resolve, reject) => {
			reject("error了");
		});
	}
	const promiseResult = foo();
	promiseResult.catch(err => console.log("result:" + err)); //result:error了 
}

 (3). 異步函數的返回值是一個對象並且實現了thenable,那么會由對象的then方法來決定

// 3.3 返回一個對象,含then方法
{
    console.log("--------3.3 返回一個對象,含then方法----------");
	async function foo() {
		console.log("foo function start~");
		console.log("內部代碼執行");
		console.log("foo function end~");
		return {
			then(resolve, reject) {
				reject("error了");
			},
		};
	}
	const promiseResult = foo();
	promiseResult.catch(err => console.log("result:" + err)); //result:error了 
}

4. 異步函數中拋出異常

   如果我們在async中拋出了異常,那么程序它並不會像普通函數一樣報錯,而是會作為Promise的reject來傳遞

{
    console.log("------4. 異步函數中拋出異常--------");
	async function foo() {
		console.log("foo function start~");
		console.log("中間代碼~");
		// 異步函數中的異常, 會被作為異步函數返回的Promise的reject值的
		throw new Error("error message");
		console.log("foo function end~");
	}
	// 異步函數的返回值一定是一個Promise
	foo().catch(err => console.log("result", err));
	console.log("后續業務~~~~~");
 
	// 運行結果:【foo function end~】沒有輸出,但是【后續業務~~~~~】輸出了
	/*     
      foo function start~
      中間代碼~
      后續業務~~~~~
      result Error: error message */
}

 

二. await關鍵字詳解

1. 使用環境

     使用await關鍵字,必須在async函數中使用,普通函數中沒法使用await關鍵字

{
	console.log("------1. await跟一個promise表達式----------");
	//封裝異步請求函數
	function requestData() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve({ status: "ok", msg: "獲取成功" });
			}, 2000);
		});
	}
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await requestData();
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		{ status: 'ok', msg: '獲取成功' }
		中間業務1
		foo end
	*/
}

2. await用法

(1). await是后面會跟上一個表達式

   這個表達式會返回一個Promise, await會等到Promise的狀態變成fulfilled狀態(resolved),之后繼續執行后面的代碼;

   如下案例:獲取到result結果后,才會繼續執行后面的代碼.

{
	console.log("------1. await跟一個promise表達式----------");
	//封裝異步請求函數
	function requestData() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve({ status: "ok", msg: "獲取成功" });
			}, 2000);
		});
	}
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await requestData();
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		{ status: 'ok', msg: '獲取成功' }
		中間業務1
		foo end
	*/
}

(2). await后面是一個普通的值,那么會直接返回這個值

{
	console.log("------2. await后面跟一個普通值----------");
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await "msg";
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		msg
		中間業務1
		foo end
	 */
}

 (3). await后面是一個thenable的對象(即一個對象,實現了then方法),那么會根據對象的then方法調用來決定后續的值;

{
	console.log("-----4. await后面跟一個實現含then方法的對象--------");
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await {
			then(resolve, reject) {
				resolve("ok");
			},
		};
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		ok
		中間業務1
		foo end
	 */
}

3. await后面的表達式,返回的Promise是reject的狀態 【重點】

      會將這個reject結果直接作為await所在函數的Promise的reject值

方案1:需要在await函數的then的第二個回調 或者 catch 中調用。

      注意:這種情況下,await后面的代碼不在執行

方案2:在foo函數中用try-catch包裹所在的業務來獲取

      注意:這種情況下,await后面的代碼不在執行

方案3:直接在await 后面的調用上 跟catch,但是雖然可以捕獲異常,但是await后面的代碼依舊會執行 【根據實際場景來決定采用這種寫法】

方案1代碼:

{
	console.log("---5.  await后面的表達式,返回的Promise是reject的狀態------");
	//封裝異步請求函數
	function requestData() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				reject({ status: "error", msg: "獲取失敗" });
			}, 2000);
		});
	}
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await requestData();
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo().catch(error => console.log(error));
	// 運行結果
	/*
		foo start
		{ status: 'error', msg: '獲取失敗' }
	 */
}

方案2代碼:

{
	console.log("---方式二 try-catch包裹------");
	//封裝異步請求函數
	function requestData() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				reject({ status: "error", msg: "獲取失敗" });
			}, 2000);
		});
	}
	// 封裝調用函數
	async function foo() {
		try {
			console.log("foo start");
			const result = await requestData();
			console.log(result);
			console.log("中間業務1");
			console.log("foo end");
		} catch (error) {
			console.log(error);
		}
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		{ status: 'error', msg: '獲取失敗' }
	 */
}

方案3代碼:

// 寫法3:直接在await 后面的調用上 跟catch
{
	console.log("---寫法3:直接在await 后面的調用上 跟catch----");
	//封裝異步請求函數
	function requestData() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				reject({ status: "error", msg: "獲取失敗" });
			}, 2000);
		});
	}
	// 封裝調用函數
	async function foo() {
		console.log("foo start");
		const result = await requestData().catch(err => console.log(err));
		console.log(result);
		console.log("中間業務1");
		console.log("foo end");
	}
	// 最終調用
	foo();
	// 運行結果
	/*
		foo start
		{ status: 'error', msg: '獲取失敗' }
		undefined
		中間業務1
		foo end
	 */
}

 

三. 異步處理方案

1. throw關鍵

(1). 作用

      用於拋出一個用戶自定義的異常;當遇到throw語句時,當前的函數執行會被停止(throw后面的語句不會執行);

(2). 用法

     A. 基本數據類型:比如number、string、Boolean

     B. 對象類型:對象類型可以包含更多的信息

     C. 返回Error類或其子類 (詳見下面)

{
	console.log("---------1. throw用法----------");
	throw "對不起,拋異常了";
	console.log("內部業務1,不能執行了");
	console.log("內部業務2,不能執行了"); 
}

{
	console.log("---------1. throw用法----------");
	throw { status: "error", msg: "出錯了" };
	console.log("內部業務1,不能執行了");
	console.log("內部業務2,不能執行了"); 
}

2. Error類型

   JavaScript已經給我們提供了一個Error類,我們可以直接創建這個類的對象

(1).Error包含三個屬性:

   A. messsage:創建Error對象時傳入的message;

   B. name:Error的名稱,通常和類的名稱一致;

   C. stack:整個Error的錯誤信息,包括函數的調用棧,當我們直接打印Error對象時,打印的就是stack;

(2).Error有一些自己的子類:

  A. RangeError:下標值越界時使用的錯誤類型;

  B. SyntaxError:解析語法錯誤時使用的錯誤類型;

  C. TypeError:出現類型錯誤時,使用的錯誤類型;

{
	console.log("---------2.Error類型----------");
	throw new Error("出錯了", "perison");
	console.log("內部業務1,不能執行了");
	console.log("內部業務2,不能執行了"); 
}
{
	console.log("---------2.TypeError類型----------");
	throw new TypeError("類型出錯了");
	console.log("內部業務1,不能執行了");
	console.log("內部業務2,不能執行了"); 
}

3. 異常處理機制

  一個函數拋出了異常,調用它的時候程序會被強制終止:

  這是因為如果我們在調用一個函數時,這個函數拋出了異常,但是我們並沒有對這個異常進行處理,那么這個異常會繼續傳遞到上一個函數調用中;

  而如果到了最頂層(全局)的代碼中依然沒有對這個異常的處理代碼,這個時候就會報錯並且終止程序的運行;

4. try-catch異常捕獲

 (1). 使用try-catch可以捕獲異常

 (2). 在ES10(ES2019)中,catch后面綁定的error可以省略。

 (3).  當然,如果有一些必須要執行的代碼,我們可以使用finally來執行:finally表示最終一定會被執行的代碼結構;

{
	console.log("---------3. try-catch捕獲異常----------");
	try {
		throw new Error("出錯了", "perison");
	} catch (error) {
		console.log(error);
	} finally {
		console.log("內部業務1");
		console.log("內部業務2");
	}
}

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM