回撥函式
JavaScript 中的回撥函式
回撥函式在 JavaScript 中很常見。JavaScript 中可以使用回撥函式,因為函式是一等公民 。
同步回撥
回撥函式可以是同步或非同步的。由於非同步回撥函式可能更復雜,因此這是一個同步回撥函式的簡單示例。
// a function that uses a callback named `cb` as a parameter
function getSyncMessage(cb) {
cb("Hello World!");
}
console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getSyncMessage(function(message) {
console.log(message);
});
console.log("After getSyncMessage call");
上面程式碼的輸出是:
> Before getSyncMessage call
> Hello World!
> After getSyncMessage call
首先,我們將逐步介紹上述程式碼的執行方式。對於那些還沒有理解回撥概念的人來說,如果你已經明白它可以隨意跳過這一段,那就更多了。首先解析程式碼,然後第一個有趣的事情是執行第 6 行,它將 Before getSyncMessage call
輸出到控制檯。然後執行第 8 行,它呼叫函式 getSyncMessage
傳送匿名函式作為 getSyncMessage
函式中名為 cb
的引數的引數。執行現在在第 3 行的 getSyncMessage
函式內完成,該函式執行剛剛傳入的函式 cb
,此呼叫在傳入的匿名函式中為名為 message
的引數傳送引數字串 Hello World
。執行然後轉到第 9 行,它將 Hello World!
記錄到控制檯。 callstack ( 另見 )擊中第 10 行然後第 4 行,最後回到第 11 行。
一般瞭解回撥的一些資訊:
- 作為回撥傳送給函式的函式可以被呼叫零次,一次或多次。這一切都取決於實施。
- 回撥函式可以同步或非同步呼叫,也可以同步和非同步呼叫。
- 就像普通函式一樣,為函式提供引數的名稱並不重要,但順序是。因此,例如在第 8 行,引數
message
可以被命名為statement
,msg
,或者如果你是像jellybean
這樣荒謬的東西。因此,你應該知道將哪些引數傳送到回撥中,以便你可以使用正確的名稱以正確的順序獲取它們。
非同步回撥
有一點需要注意 JavaScript 是它預設是同步的,但也有在環境(瀏覽器,Node.js 的,等等),可以使人們在非同步模式(有更多有關給定的 API 在這裡 )。
在接受回撥的 JavaScript 環境中非同步的一些常見事物:
- 活動
- setTimeout
- setInterval
- fetch API
- 承諾
此外,任何使用上述函式之一的函式都可以用一個帶回撥函式的函式進行包裝,然後回撥將是一個非同步回撥(儘管用一個帶回撥函式的函式包裝一個 promises 可能會被認為是一個反模式有更多首選方式來處理承諾)。
因此,給定該資訊,我們可以構造類似於上述同步函式的非同步函式。
// a function that uses a callback named `cb` as a parameter
function getAsyncMessage(cb) {
setTimeout(function () { cb("Hello World!") }, 1000);
}
console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getAsyncMessage(function(message) {
console.log(message);
});
console.log("After getSyncMessage call");
其中將以下內容列印到控制檯:
> Before getSyncMessage call
> After getSyncMessage call
// pauses for 1000 ms with no output
> Hello World!
行執行轉到第 6 行日誌“在 getSyncMessage 呼叫之前”。然後執行轉到第 8 行,呼叫 getAsyncMessage 並使用 param cb
的回撥。然後執行第 3 行,呼叫 setTimeout,回撥作為第一個引數,數字 300 作為第二個引數。setTimeout
做它做的任何事情,並保持該回撥,以便它可以在 1000 毫秒後呼叫它,但在設定超時之後,在它暫停 1000 毫秒之前,它將執行返回到它停止的位置,所以它轉到第 4 行,然後第 11 行,然後暫停 1 秒,然後 setTimeout 呼叫其回撥函式,該函式將執行返回到第 3 行,其中呼叫 getAsyncMessages
回撥,其引數 message
的值為 Hello World
,然後在第 9 行記錄到控制檯。
Node.js 中的回撥函式
NodeJS 具有非同步回撥,通常為你的函式提供兩個引數,有時通常稱為 err
和 data
。閱讀檔案文字的示例。
const fs = require("fs");
fs.readFile("./test.txt", "utf8", function(err, data) {
if(err) {
// handle the error
} else {
// process the file text given with data
}
});
這是一次呼叫的示例。
即使你只是記錄它或丟擲它,以某種方式處理錯誤也是一種很好的做法。如果丟擲或返回則不需要 else,只要你通過執行丟擲或返回等操作停止執行 if 中的當前函式,就可以刪除 else 以減少縮排。
儘管看到 err
,data
可能並不常見,但是你的回撥可能並不總是使用這種模式,最好是檢視文件。
另一個示例回撥來自快速庫(表示式 4.x):
// this code snippet was on http://expressjs.com/en/4x/api.html
const express = require('express');
const app = express();
// this app.get method takes a url route to watch for and a callback
// to call whenever that route is requested by a user.
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
此示例顯示了多次呼叫的回撥。回撥提供了兩個物件作為 params 這裡命名為 req
和 res
這些名稱分別對應於請求和響應,並且它們提供了檢視進入的請求並設定將傳送給使用者的響應的方法。
正如你所看到的,有各種方法可以使用回撥來在 JavaScript 中執行同步和非同步程式碼,並且回撥在 JavaScript 中非常普遍。