前端基礎
-JavaScript 的 async / await 到底在幹嘛?前端非同步的簡潔寫法。
this.web
甚麼是非同步函數 async / await
saync / await 是 ES8 新增的語法,它允許我們以同步的方式編寫非同步程式碼,讓我們更容易地管理非同步操作,編寫出更清晰、簡潔、易讀的程式碼。
來看一個最簡單的例子。
let p = new Promise((resolve, reject)
=> setTimeout(resolve, 1000, 3));
p.then((x) => console.log(x)) // 一秒後 3這個 promise 會在 1 秒後解決且數值為 3,但我們必須要把處理結果的函數都放在 .then() 中,有時候很不方便。
而 async / await 就是想解決 .then() 寫起來不方便的問題 👇
async function asyncFn() {
let p = await new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 3);
});
console.log(p);
}
asyncFn();有沒有覺得更像接近平常寫程式的方式了,而且也不用寫一堆的 .then()。
簡單解釋上面的程式一就是 async 關鍵字告訴引擎說這個函數有非同步的程式碼,而 await 會等待 promise 的結果,並暫停接下來的程式碼,當結果回傳後才會繼續執行接下來的程式碼。
接下來讓我們更深入講講 async、await 這兩個關鍵字。
async
async 可以用在聲明式、表達式、箭頭函數以及類的方法上
async function a() {}
let b = async function() {};
let c = async () => {};
class D {
async d() {}
}且他會返回一個 promise 物件
async function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('John')) //Promise {<fulfilled>: 'Hello, John!'}
greet('John')
.then(result => console.log(result));不知道你也沒有想過這個問題,上面說 async 可以減少 .then(),那為甚麼 async 又要返回 promise 物件呢,是因為在 JS 中,Promise 是用來處理非同步操作的標準方式,而 async 函數本質上也是處理非同步的方式,所以返回的結果自然是 Promise。
await
await 關鍵字只能用在 async function 內部,await 會等待右側的 promise 處理完成並返回處理的結果,如果 await 等待的不是 promise 則直接返回值本身
async function asyncFn() {
let promise = await new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'promise');
});
let notPromise = await 'notPromise';
console.log(promise, notPromise);
}
asyncFn(); // promise notPromise補充: JavaScript 在碰到 await 關鍵字時,會記錄在哪裡暫停執行。等到 await 右邊的值可用了,JavaScript 會向消息隊列中推送一個任務,這個任務會恢復異步函數的執行。
所以就算 await 右側不是 promise 函數時也會被當作非同步處理
async function foo() {
console.log(2);
await null;
console.log(4);
}
console.log(1);
foo();
console.log(3);
// 1
// 2
// 3
// 4這部份如果不太理解可以去看看 event loop。
錯誤處理
要注意的是,await 只能接受成功的 promise,若是處理失敗就會報錯
async function test() {
let err = await Promise.reject('錯誤')
console.log(err)
};
test()
// Uncaught (in promise) 錯誤必須要使用 try...catch... 的方式去處理錯誤
async function test(condition) {
try {
let p = await new Promise((resolve) => {
if(condition) {
resolve('成功')
} else {
throw new Error('錯誤');
}
});
console.log(p)
} catch(e) {
console.log(e)
}
}
test(true) // 成功
test(false) //Error: 錯誤await 的限制
此外,await 不能用在巢狀函數(嵌套函數)中使用 👇
async function test() {
function innerFn1() {
return await Promise.resolve(2);
}
const innerFn2 = function() {
return await Promise.resolve(2);
}
const innerfn3 = () => {
return await Promise.resolve(3);
};
}
test()上面全部都錯誤的使用方式。
和 Promise 的比較
async/await 的優勢在處理多個 promise 組成的 then,有點像 promise 進一步的優化,下面我們來比較兩種寫法。
function fecthSomething(ms, data) { // 模擬非同步
return new Promise((resolve)=>{
setTimeout(() => {
resolve(data);
}, ms);
})
}
/*
* Promise
*/
let p1 = fecthSomething(1000, 'p1');
let p2 = fecthSomething(2000, 'p2');
p1.then((res) => {
console.log(res)
p2.then((res) => {
console.log(res)
console.log('all done')
})
})
// p1
// p2
// 3 秒後 all done
/*
* async/await
*/
async function asyncAwaitFn() {
let res1 = await fecthSomething(1000, 'a1');
let res2 = await fecthSomething(2000, 'a2');
console.log('all done' ,res1, res2);
}
asyncAwaitFn() // 3 秒後 all done a1 a2有沒有感受到 async 和 promise 的差別呢?
小結
其實 async / await 就像是 promise.then() 的另一種寫法,寫起來就像同步的程式碼,也更簡潔,但處理錯誤的方式要用 try...catch...
不過 async 和 Promise 的寫法其實沒有誰一定比較好,雖然 async 寫起來更簡潔, 但 Promis 有 all()、race()、allSettled() 等好用的寫法, 某些情況下 .then() 可能會比較方便和靈活,所以要使用哪個還是看個人習慣和情境。
那今天就介紹到這篇,希望這幾篇非同步的文章,能讓你更清楚知道 JS 的非同步觀念,我們下篇貼文見~