如何实现可重用的回调函数

How to implement reusable callback functions

我是 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 将它们传递给回调。这似乎也不是一个好计划。 是否可以更改函数的变量范围,使其从执行点而不是定义点起像闭包一样的作用?如果没有,什么是 mod 化回调代码以便重复使用的最佳方法?

一般来说,必须创建另一个可以访问闭包的函数在线。您可以通过一个简单的匿名函数在线创建函数,该函数将一些参数作为参数传递给父回调函数,同时接受其余参数(即部分函数),或者使用 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); );
}

这里是一个没有闭包也没有嵌套的嵌套回调示例。 它抓取指定的页面,找到第三个 link,然后向用户显示 link 的来源。

在这种情况下,当 运行 从这里进入控制台并提供主页时,注销页面是第三个 link,它的内容会被提醒。

所有回调都是兄弟,没有外部状态变量,只有纯函数式异步:

// 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 并避免了 promises 只是为了展示这种交互方式如何在没有任何并发​​症的情况下执行。甚至 ajax 助手也使用非嵌套函数将 responseText 提供给 "core" 回调,而不是事件或整个 xhr 对象。