我正在尝试第一次理解回调。在我看到的所有示例中,回调总是作为参数传递。这是一个常见的示例:
let result = 0;
function add(num1, num2, callback) {
setTimeout(() => {
result = num1 + num2;
callback();
}, 2000);
}
function logResult() {
console.log(result);
}
add(4, 5, logResult); // here's the callback passed as argument
使用以下代码可以获得相同的结果。而且它不需要将回调作为参数传递。
let result = 0;
function add(num1, num2) {
setTimeout(() => {
result = num1 + num2;
logResult();
}, 2000);
}
function logResult() {
console.log(result);
}
add(4, 5);
将它们作为参数传递只是为了提高可读性和更好地理解代码吗?还是我想念的东西?请有人能启发我吗?
答案 0 :(得分:6)
您的示例中根本不需要回调,您可以这样做:
function add(num1, num2) {
setTimeout(() => { // thats a callback too, just saying ...
const result = num1 + num2; // don't leak variables, declare them!
console.log(result);
}, 2000);
}
但是编程只不过是创建可重用代码,然后可以将其组成更复杂的程序。因此,您不想将add
的使用限制为记录结果,而是可以通过接受回调来完成各种任务:
add(1, 2, (result) => {
add(result, 5, (result2) => {
alert(result2);
});
});
答案 1 :(得分:3)
它不需要将回调作为参数传递。
是的...
setTimeout(() => {
使用箭头函数定义的一个回调传递给setTimeout
。
(我知道您的意思是函数callback
,但这仍然是一个回调并证明了我的观点)
由于setTimeout
不是您定义的函数,因此在可能达到的范围内定义函数的唯一方法是使其成为全局函数。
然后,如果您要同时运行两个setTimeout实例,则将第一个回调分配给第一个全局回调,然后将第二个回调分配给第二个回调……好吧……您会被困住。
答案 2 :(得分:3)
回调是一种解耦代码的机制。
例如,假设API的add
部分带有第一个代码,我可以编写:
add(4, 5, console.log);
add(4, 5, alert);
add(4, 5, writeOnTheFileSystemIfNodeJS);
add(4, 5, addToTheDOM);
// etc.
这与您的第二个代码是不可能的:它太耦合了,所以我需要使用不同版本的add
函数来完成以上所有四个操作:addConsole
,{{1} }等。不仅如此:通过回调,您还提供了一种机制来处理您无法预期的逻辑。也许开发人员想将结果添加到canvas元素上,而您没有提供addAlert
东西。但是使用回调,即使原则上不是出于此目的而设计的,也可以实现。
但是请注意,如今对于此类操作-一次 发生-您可能会使用Promises,因为它们与await / async的配合非常好可能会多次发生,您可能想使用事件(例如addCanvas
)或流-在关闭功能中,由于{{3} }。
答案 3 :(得分:1)
首先,请注意:回调被认为是不良做法。现在,我们有了更好的方法来处理这种事情。作为语言规范的一部分,我们有Promise
个。就外部库而言,我们还有Observable
个。
在成为规范的一部分之前,Promise
是使用 回调构建的,但是为您提供了一种更具可读性的处理方式,尤其是在回调链方面。
非常特别地是因为通常在库代码中使用回调,而不是在您的主代码中使用回调,所以库开发人员正在添加一种方法来为您的行为添加自定义功能。至于在您自己的代码中使用回调...根据应用程序的当前状态以及该函数的调用者,您可能希望使用不同的回调。底线是separation of concerns
是您需要熟悉的重要概念。
例如,
function showModal(whichModal) {
someLibrary.modal(whichModal).show();
switch (whichModal) {
case 'createUser':
someUserLogic();
break;
case 'createProject':
someProjectLogic();
break;
}
}
function createUser() {
showModal('createUser');
}
function createProject() {
showModal('createProject');
}
vs
function showModal(whichModal, postShowCallback) {
someLibrary.modal(whichModal).show();
postShowCallback();
}
function createUser() {
showModal('createUser', someUserLogic);
}
function createProject() {
showModal('createProject', someProjectLogic);
}
您可以看到第一个示例很快就会失控,第二个示例可以很好地解决这个问题
答案 4 :(得分:0)
将回调作为参数传递的原因之一是避免范围界定问题。您的示例中的logResult
函数可能未在您调用add
的任何地方定义。另外,logResult
可能会发生突变。请考虑以下内容:
let result = 0;
function add(num1, num2) {
setTimeout(() => {
result = num1 + num2;
logResult();
}, 2000);
}
function logResult() {
console.log(result);
}
add(4, 5);
function logResult() {
console.log(2);
}
在以上代码段中,logResult
函数为hoisted,原始版本被覆盖。请注意,尽管在第二个add(4, 5)
声明之前调用了logResult
,但这还是发生了。
通过接受回调参数可以避免此问题和其他范围问题。
function add(a, b, callback) {
setTimeout(() => {
callback(a + b);
}, 2000);
}
add(4, 5, (result) => setTimeout(() => console.log(result), 2000));