一、async和await
async和await的概念
1)async 函數(shù)是 Generator 函數(shù)的語法糖,使用 關(guān)鍵字 async 來表示,在函數(shù)內(nèi)部使用 await 來表示異步 2)ES7 提出的async 函數(shù),終于讓 JavaScript 對于異步操作有了終極解決方案 3)async 作為一個關(guān)鍵字放到函數(shù)的前面,用于表示函數(shù)是一個異步函數(shù),該函數(shù)的執(zhí)行不會阻塞后面代碼的執(zhí)行 4)await是等待,只能放到async函數(shù)里面,在后面放一個返回promise對象的表達式 5)async和await是為了解決大量復(fù)雜不易讀的Promise異步的問題
async函數(shù)的改進
1)內(nèi)置執(zhí)行器,Generator 函數(shù)的執(zhí)行必須依靠執(zhí)行器,而 Aysnc 函數(shù)自帶執(zhí)行器,調(diào)用方式跟普通函數(shù)的調(diào)用一樣 2)更好的語義,async 和 await 相較于 * 和 yield 更加語義化 3)更廣的適用性,co 模塊約定,yield 命令后面只能是 Thunk 函數(shù)或 Promise對象,而 async 函數(shù)的 await 命令后面則可以是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同于同步操作) 4)返回值是 Promise,async 函數(shù)返回值是 Promise 對象,比 Generator 函數(shù)返回的 Iterator 對象方便,可以直接使用 then() 方法進行調(diào)用 5)async方式,流程清晰,直觀、語義明顯,操作異步流程就如同操作同步流程, async 函數(shù)自帶執(zhí)行器,執(zhí)行的時候無需手動加載。對于Promise的方式,如果處理流程復(fù)雜,整段代碼將會充滿then,不然很好的表示流程。對于Generator 方式,函數(shù)的執(zhí)行需要依靠執(zhí)行器,每次都需要通過 g.next() 的方式去執(zhí)行
async和await的實例
1)async 作為一個關(guān)鍵字放到函數(shù)的前面,用于表示函數(shù)是一個異步函數(shù),該函數(shù)的執(zhí)行不會阻塞后面代碼的執(zhí)行 實例代碼:
async function timeout(){return "hello word";}timeout();// Promise __proto__: Promise [[PromiseStatus]]: "resolved" [[PromiseValue]]: "hello word"//async返回的是 promise 對象console.log(timeout());// 我在后面,但是我是先執(zhí)行的console.log("我在后面,但是我是先執(zhí)行的");
2)async聲明的函數(shù)的返回本質(zhì)上是一個Promise,async函數(shù)內(nèi)部會返回一個Promise對象,then方法回調(diào)函數(shù)的參數(shù) 實例代碼:
// hello world11async function f1(){return "hello world11";};f1().then((v)=>console.log(v));
3)await的本質(zhì)是可以提供等同于”同步效果“的等待異步返回能力的語法糖,用await聲明的Promise異步返回,必須“等待”到有返回值的時候,代碼才繼續(xù)執(zhí)行下去 實例代碼:
/* hello1我是hello2輸出 hello1*/const test = async()=>{let message = "hello1";let result = await message;console.log(result);console.log("我是hello2");return result;};test().then(result =>{console.log("輸出",result);});
4)async 函數(shù)內(nèi)部的實現(xiàn)原理是resolved,如果函數(shù)內(nèi)部拋出錯誤, 則會導(dǎo)致返回的 Promise 對象狀態(tài)變?yōu)?reject 狀態(tài),promise 對象有一個catch 方法進行捕獲,被 catch 方法回調(diào)函數(shù)接收到 實例代碼:
async function timeout2(flag){if(flag){return "hello world";}else{throw "failed";}}// 如果函數(shù)內(nèi)部拋出錯誤, promise 對象有一個catch 方法進行捕獲timeout2(false).catch(err => {console.log(err);});// hello word// 調(diào)用Promise.resolve() 返回promise 對象console.log(timeout2(true));// Uncaught (in promise) failed// 調(diào)用Promise.reject() 返回promise 對象console.log(timeout2(false));
5)async必須聲明的是一個function,await就必須是在這個async聲明的函數(shù)內(nèi)部使用,必須是直系,作用域鏈不能隔代,在后面放一個返回promise對象的表達式 實例代碼:
// 需求:2s后讓數(shù)值乘以2function doubleAfter(num){return new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2*num)},2000);});}// 60// 等待2s后,promis開始resolve,返回值async function test(){let result = await doubleAfter(30);console.log(result);}test();// 240//等待6s后,promis開始resolve,返回值async function test2(){let first = await doubleAfter(30);let second = await doubleAfter(40);let third = await doubleAfter(50);console.log(first+second+third);}test2();
6)函數(shù)的錯誤處理,當 async 函數(shù)中只要一個 await 出現(xiàn) reject 狀態(tài),則后面的 await 都不會被執(zhí)行,可以添加 try/catch 實例代碼:
let a;async function f5(){await Promise.reject("error");a = await 1;}//f5().then(v=>console.log(a));// try/catch 解決// Uncaught (in promise) ReferenceError: error is not definedat f6 (test13.html:124)let a3;async function f6(){try{await Promise.reject("error");}catch{console.log(error);}a3 = await 123;return a3;}f6().then((v)=>console.log(a3));
async/await的實戰(zhàn)
1)需求分析:有兩個延時函數(shù),先延時1秒,在延遲2秒,再延時1秒,最后輸出“完成” 2)實例代碼:
// 延時函數(shù)
const setDelay = (millisecond) => {return new Promise((resolve, reject)=>{if (typeof millisecond != 'number') reject(new Error('參數(shù)必須是number類型'));setTimeout(()=> {resolve(`我延遲了${millisecond}毫秒后輸出的`)}, millisecond)})
}
const setDelaySecond = (seconds) => {return new Promise((resolve, reject)=>{if (typeof seconds != 'number' || seconds > 10) reject(new Error('參數(shù)必須是number類型,并且小于等于10'));setTimeout(()=> {resolve(`我延遲了${seconds}秒后輸出的,注意單位是秒`)}, seconds * 1000)})
}/* 我延遲了${millisecond}毫秒后輸出的我延遲了${seconds}秒后輸出的,注意單位是秒我延遲了${millisecond}毫秒后輸出的完成*/
(async ()=>{const result = await setDelay(1000);console.log(result);console.log(await setDelaySecond(2));console.log(await setDelay(1000));console.log('完成了');
})()
二、async和await的面試題總結(jié)
談?wù)剬?async/await 的理解,async/await 的實現(xiàn)原理是什么? 1)async/await 就是 Generator 的語法糖,使得異步操作變得更加方便 2)async 函數(shù)就是將 Generator 函數(shù)的星號(*)替換成 async,將 yield 替換成await 3)async 是 Generator 的語法糖 ,這個糖體現(xiàn)在這幾個方面:
async函數(shù)內(nèi)置執(zhí)行器,函數(shù)調(diào)用之后,會自動執(zhí)行,輸出最后結(jié)果,而Generator需要調(diào)用next或者配合co模塊使用 更好的語義,async和await,比起星號和yield,語義更清楚了,async表示函數(shù)里有異步操作,await表示緊跟在后面的表達式需要等待結(jié)果 更廣的適用性,co模塊約定,yield命令后面只能是 Thunk 函數(shù)或 Promise 對象,而async 函數(shù)的 await 命令后面,可以是 Promise 對象和原始類型的值 返回值是Promise,async函數(shù)的返回值是 Promise 對象,Generator的返回值是 Iterator,Promise 對象使用起來更加方便 4)async/await 函數(shù)的實現(xiàn)原理,就是將 Generator 函數(shù)和自動執(zhí)行器,包裝在一個函數(shù)里 5)實例代碼分析:
function my_co(it) {return new Promise((resolve, reject) => {function next(data) {try {var { value, done } = it.next(data);}catch(e){return reject(e);}if (!done) { //done為true,表示迭代完成//value 不一定是 Promise,可能是一個普通值。使用 Promise.resolve 進行包裝。Promise.resolve(value).then(val => {next(val);}, reject);} else {resolve(value);}}next(); //執(zhí)行一次next});
}
function* test() {yield new Promise((resolve, reject) => {setTimeout(resolve, 100);});yield new Promise((resolve, reject) => {// throw Error(1);resolve(10)});yield 10;return 1000;
}my_co(test()).then(data => {console.log(data); //輸出1000
}).catch((err) => {console.log('err: ', err);
});
使用 async/await 需要注意什么? 1)await 命令后面的Promise對象,運行結(jié)果可能是 rejected,此時等同于 async 函數(shù)返回的 Promise 對象被reject。因此需要加上錯誤處理,可以給每個 await 后的 Promise 增加 catch 方法;也可以將 await 的代碼放在 try…catch 中 。 2)多個await命令后面的異步操作,如果不存在繼發(fā)關(guān)系,最好讓它們同時觸發(fā) 實例代碼:
//下面兩種寫法都可以同時觸發(fā)
//法一
async function f1() {await Promise.all([new Promise((resolve) => {setTimeout(resolve, 600);}),new Promise((resolve) => {setTimeout(resolve, 600);})])
}
//法二
async function f2() {let fn1 = new Promise((resolve) => {setTimeout(resolve, 800);});let fn2 = new Promise((resolve) => {setTimeout(resolve, 800);})await fn1;await fn2;
}
3)await命令只能用在async函數(shù)之中,如果用在普通函數(shù),會報錯 4)async 函數(shù)可以保留運行堆棧 實例代碼:
/
* 函數(shù)a內(nèi)部運行了一個異步任務(wù)b()。當b()運行的時候,函數(shù)a()不會中斷,而是繼續(xù)執(zhí)行。
* 等到b()運行結(jié)束,可能a()早就* 運行結(jié)束了,b()所在的上下文環(huán)境已經(jīng)消失了。
* 如果b()或c()報錯,錯誤堆棧將不包括a()。
*/
function b() {return new Promise((resolve, reject) => {setTimeout(resolve, 200)});
}
function c() {throw Error(10);
}
const a = () => {b().then(() => c());
};
a();
/**
* 改成async函數(shù)
*/
const m = async () => {await b();c();
};
m();
?
總結(jié)
以上是生活随笔 為你收集整理的“约见”面试官系列之常见面试题第三十二篇之async和await(建议收藏) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。