如何在 JavaScript 中等待函式完成
-
JavaScript 中的
Sync
和Async
-
在 JavaScript 中使用
回撥
來等待一個函式的完成 -
在 JavaScript 中使用
promises
來等待一個函式的完成 -
使用
async/await
等待一個函式完成後再繼續執行
本教程將介紹 JavaScript 的 回撥
、承諾
和 Async/await
,並告訴你如何等待一個非同步函式完成後再繼續執行。
要了解什麼是 Promises
和 Async/await
,我們首先要了解 JavaScript 中的 Sync
和 Async
函式是什麼。
JavaScript 中的 Sync
和 Async
同步程式設計一次執行一條命令。當我們呼叫一個函式執行一個長期執行的動作時,它會停止程式,直到它完成。
JavaScript 是傳統的單執行緒,即使是多核也是如此。我們可以讓它只在一個叫做主執行緒的單執行緒上執行任務。
這樣的同步行為是多執行緒的限制因素,但可以幫助使用者編寫程式碼而不用擔心併發問題。
而在非同步程式設計中,長期執行的動作會在主執行緒以外的另一個執行緒中執行,所以主執行緒的執行不會被阻塞。當該長期執行的函式完成後,主程式會被告知並獲得訪問結果。
JavaScript 中的大多數 I/O 基元都是非阻塞的。網路請求、檔案系統操作都是非阻塞操作。被阻塞是 JavaScript 中的例外。
由於 JavaScript 是基於非同步程式設計技術,所以有多種方法,如 回撥
、承諾
和 async/await
,使你能夠把你的函式按順序執行。這樣,一個程式碼塊或一個函式不會在另一個特定函式完成之前被執行。
上圖顯示了兩個函式非同步和同步執行的明顯變化。
在 JavaScript 中使用 回撥
來等待一個函式的完成
如果我們有同步語句,那麼執行這些語句之後就可以直接執行。
function one() {
console.log('I am function One');
}
function Two() {
console.log('I am function Two');
}
one();
Two();
輸出:
I am function One
I am function Two
假設我們要執行兩個函式,functionOne()
和 functionTwo()
,這樣 functionOne()
應該在執行完 functionTwo()
裡面的一些非同步語句後執行。
function functionOne(_callback) {
// do some asynchronus work
_callback();
}
function functionTwo() {
// do some asynchronus work
functionOne(() => {
console.log('I am a callback');
});
}
functionTwo();
在執行上述程式碼時,最後在控制檯中列印的是 I am a callback
。著名的 callback
例子是 setTimeout
函式,在超時後要執行一個處理函式。
function testCallBack() {
console.log('log before use setTimeout function');
setTimeout(() => {
console.log('inside timeout');
}, 5000);
console.log('log after use setTimeout function');
}
testCallBack();
輸出:
log before use setTimeout function
log after use setTimeout function
inside timeout
在 JavaScript 中使用 promises
來等待一個函式的完成
promise
是一個代表非同步操作的最終實現或失敗的物件,我們用一個或多個 then
語句將實現回撥附加到 promise
上。我們用一個或多個 then
語句將實現回撥附加到 promise
上,並在 catch
中呼叫錯誤處理回撥。
doFirstThing()
.then(result => doSecondThing(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log('The final result thing' + finalResult);
})
.catch(failureCallbackfunction);
}
像上面的例子一樣把 then
和 catch
語句鏈起來,是承諾的優勢之一。我們承諾一旦 doFirstThing()
被滿足,就會 doSecondThing(result)
。then
的引數是可選的,但如果你必須返回一個結果,則需要。
如果出現錯誤,瀏覽器會在鏈的最後尋找 catch
並執行。這和著名的 try-catch
非常相似。
下面的例子將幫助我們理解 promises
鏈,並告訴我們如何等待一個具有非同步行為的函式完成執行後才能繼續執行。
var resolvedFlag = true;
let mypromise = function functionOne(testInput) {
console.log('Entered function');
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Inside the promise');
if (resolvedFlag == true) {
resolve('Resolved');
} else {
reject('Rejected')
}
}, 2000);
});
};
mypromise()
.then((res) => {console.log(`The function recieved with value ${res}`)})
.catch((error) => {
console.log(`Handling error as we received ${error}`);
});
輸出:
Entered function
Inside the promise
The function received with value Resolved
建立一個承諾可以像返回一個新的 Promise()
一樣簡單。Promise()
建構函式接收一個函式作為引數,它應該有兩個引數-resolve
和 reject
。
萬一我們的事件實現了,需要返回結果,當成功完成我們正在做的事情時,我們使用 resolve()
函式。但如果發生了錯誤,需要處理,我們使用 reject()
將錯誤傳送到 catch
。
我們設定標誌 resolvedFlag = true
來模擬 catch
中的錯誤處理。如果 resolvedFlag
設定為 false
,則呼叫 reject()
函式,在 catch
塊中處理錯誤。
輸出:
Entered function
Inside the promise
Handling error as we received Rejected
使用 async/await
等待一個函式完成後再繼續執行
在 JavaScript 的非同步環境中,另一種等待函式執行後再繼續執行的方法是使用 async/wait
。
async
函式是由 async
關鍵字宣告的函式,而在 async
函式內部只允許使用 await
關鍵字,用於暫停 async 函式內部的進度,直到實現或拒絕基於承諾的非同步操作。
async
和 await
關鍵字以更簡潔的風格實現了基於承諾的非同步行為。
讓我們來了解一下 async/await
是如何工作的。我們要等待的函式應該返回一個 Promise 類的例項,在呼叫它之前使用 await 關鍵字等待它執行。如上所述,包含 await
語句的函式必須與 async
語句一起宣告。
下面的例子展示瞭如何等待那個基於承諾的函式完成後再繼續執行。
function testAsync() {
return new Promise((resolve, reject) => {
// here our function should be implemented
setTimeout(() => {
console.log('Hello from inside the testAsync function');
resolve();
;
}, 5000);
});
}
async function callerFun() {
console.log('Caller');
await testAsync();
console.log('After waiting');
}
callerFun();
輸出:
Caller
Hello from inside the testAsync function
After waiting