回调函数
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 中非常普遍。