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