我是JavaScript的新手,我在节点工作,需要很好地理解异步编程和回调设计。我发现即使你的回调是多层深度,使用嵌入式函数也很容易。您的嵌入式回调最终会被关闭。
但是,当你有多个回调层,其中许多回调在执行路由中是相似的,你最终会在不同的回调链中反复重写大量的回调代码。例如,如果下面的mycb1和mycb2定义移到A之外,则它们不再具有对A的变量的隐式访问权限,因此不再用作闭包。
嵌入式定义的示例,它们用作闭包。
mod.A=function(){
var mycb1=function(err){
if (!err){
var mycb2=function(err){
cb(err);
};
mod.foo2(args,mycb2);
}
else{cb(err);}
};
mod.foo1(args,mycb1);
}
mod.foo1 = function(args,cb){
//do some work
cb(err);
};
mod.foo2 = function(args,cb){
//do some work
cb(err);
}
//execute
mod.A();
我想要执行以下操作但能够更改mycb1和mycb2函数的变量范围,以便它们可以用作调用它们的闭包。 例如:
var mycb2=function(err){
...
cb(err);
};
var mycb1=function(err){
if (!err){
mod.foo2(args,mycb2); //but have it act like a closure to mycb1
}
else{cb(err);}
};
mod.A=function(){
...
mod.foo1(args,mycb1); //but have it act like a closure to A
}
mod.foo1 = function(args,cb){
//do some work
cb(err);
}
mod.foo2 = function(args,cb){
//do some work
cb(err);
}
我知道我可以实现一个设计,它可以在mod级别设置变量,因此可以访问mod级别的函数。然而,这似乎在某种程度上污染了具有变量的mod范围,而变量可能只被其中的一些变量所使用。方法。我也知道我可以传入变量,使它们在执行时可以被回调访问。但是,如果我理解JS和回调如何工作,我将不得不将它们传递给fooX然后让foo将它们传递给回调。这看起来也不是一个好计划。 函数的变量范围是否可以改变,因此它从执行点而不是它的定义点就像一个闭包?如果没有,那么模块化你的回调代码以便重用它的最佳方法是什么?
答案 0 :(得分:7)
通常,无需创建可以访问闭包的其他函数 in-line 。您可以通过一个简单的匿名函数来创建函数,该函数将一些参数传递给父回调作为参数,同时接受其余的(即部分函数),或者使用Function.bind()创建部分函数本身。
例如,如果您最初有:
function(...) {
// closure vars x1, y1
foo.bar( function(result) {
// do something with x1 and y1
});
}
您可以将其解压缩为:
var callback = function(x1, y1, result) {
// do something with x1 and y1
};
function(...) {
// closure vars x1, y1
// option 1: make your own partial function
foo.bar( function(result) { return callback(x1, y1, result); });
// with ES6: foo.bar( (result) => callback(x1, y1, result); });
// option 2: use Function.bind
foo.bar( callback.bind(this, x1, y1); );
}
答案 1 :(得分:1)
这是一个嵌套的回调示例,没有闭包,也没有嵌套。 它抓取指定的页面,找到第三个链接,然后显示该用户链接的来源。
在这种情况下,当从控制台中运行并输入主页时,退出页面是第三个链接,并且它的内容会被警告。
所有回调都是兄弟,并且没有外部状态变量,只是纯函数异步:
// look ma, no nesting!
function ajax(a, c) {
var e = new XMLHttpRequest;
e.onload= ajaxOnload.bind(this, c, e);
e.open( "GET", a, !0);
e.send();
return e
}
function ajaxOnload(c, e) {
c(e.responseText, e);
}
function cbFind3(cb, s){
var t=document.createElement("body");
t.innerHTML=s;
var lnk= t.getElementsByTagName("a")[3].href;
ajax(lnk, cb);
}
function grabThirdLinkSource(url, cb){
ajax(url, cbFind3.bind(this, cb));
}
grabThirdLinkSource("/", alert);
它不是最有用的示例,但它确实展示了如何使用bind()跨调用链接函数。我使用vanilla ajax并避免承诺只是为了展示这种交互方式如何执行而没有任何复杂性。即使是ajax助手也使用非嵌套函数将responseText提供给"核心"回调而不是事件或整个xhr对象。