一. 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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
