理解延迟执行链的语法

And*_*ols 28 javascript node.js deferred

我正在学习JavaScript - 真正学习JavaScript.我来自PHP背景,所以一些JavaScript概念对我来说仍然是新的,尤其是异步编程.这个问题可能已经多次回答,但我找不到答案.这可能是因为我甚至不知道如何通过展示一个例子来提出问题.所以这里是:

从npm 使用deferred包时,我看到以下示例:

delayedAdd(2, 3)(function (result) {
  return result * result
})(function (result) {
  console.log(result); // 25 
});
Run Code Online (Sandbox Code Playgroud)

他们将此称为链接,它实际上是有效的,因为我目前正在使用此代码来检查承诺何时解决或被拒绝.即使他们称之为链接,它也会让我想起像Swift那样的尾随闭包.

我真的不明白这是什么类型的链接,因为我们有一个函数调用,然后紧接着,括在括号中的匿名函数.

所以我想我有两个问题.

  1. 这是什么模式?
  2. 它是如何工作的?这可能是一个很复杂的问题,但我想知道某些事情是如何运作的,所以当有人问我这件事时我可以给他们一个详细的解释.

这是delayedAdd函数:

var delayedAdd = delay(function (a, b) {
  return a + b;
}, 100);
Run Code Online (Sandbox Code Playgroud)

它使用以下功能:

var delay = function (fn, timeout) {
  return function () {
    var def = deferred(), self = this, args = arguments;

    setTimeout(function () {
      var value;
      try {
        value = fn.apply(self, args));
      } catch (e) {
        def.reject(e);
        return;
      }
      def.resolve(value);
    }, timeout);

    return def.promise;
  };
};
Run Code Online (Sandbox Code Playgroud)

mhl*_*hlz 22

它实际上很容易理解.让我们来看看评估表达式时发生了什么:

首先delayedAdd(2, 3)调用该函数.它会做一些事情,然后返回."魔法"是关于它的返回值,它是一个function.更确切地说,它是一个至少需要一个参数的函数(我会回过头来看).

现在我们评估delayedAdd(2, 3)了一个函数,我们得到了代码的下一部分,即左括号.打开和关闭括号当然是函数调用.所以我们将调用delayedAdd(2, 3)刚刚返回的函数,我们将传递一个参数,这是接下来定义的:

那个论点是另一个函数(正如你在你的例子中看到的那样).此函数也接受一个参数(计算结果)并返回它自身乘以.

第一次调用delayedAdd(2, 3)返回的这个函数返回另一个函数,我们将再次调用另一个函数(链的下一部分).

总而言之,我们通过将代码传递给delayedAdd(2, 3)返回的函数来构建一系列函数.这些函数将返回我们可以再次传递函数的其他函数.

我希望这会使它的工作方式有点明确,如果不是随意提出更多问题.


Leo*_*Leo 14

mhlz的答案很清楚.作为补充,我在这里撰写一篇文章,delayedAdd以便您更好地理解这一过程

function delayedAdd(a, b) {
  var sum = a + b
  return function(f1) {
    var result1 = f1(sum)
    return function(f2) {
      f2(result1)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的示例代码中,您传递的函数f1是:

function (result) {
  return result * result
}
Run Code Online (Sandbox Code Playgroud)

f2是:

function (result) {
  console.log(result)
}
Run Code Online (Sandbox Code Playgroud)


col*_*sar 7

函数是JS中的一等公民 - 这意味着(除其他外),他们可以扮演实际参数和函数返回值的角色.您的代码片段将函数映射到函数.

链接调用中函数的签名可能如下所示.

delayedAdd: number -> fn                     // returns function type a
         a: fn ( number -> number) -> fn     // returns function type b
         b: fn ( number -> void )  -> void   // returns nothing ( guessing, cannot know from your code portion )
Run Code Online (Sandbox Code Playgroud)

通用设置

当然,JS是一种弱类型语言,因此列出的签名是通过猜测从代码片段中派生出来的.除了检查源代码之外,无法知道代码是否实际执行了上述建议.

鉴于这出现在"链接"的背景下,签名可能看起来像这样:

delayedAdd: number x number -> fn (( fn T -> void ) -> ( fn T -> void ))
Run Code Online (Sandbox Code Playgroud)

这意味着delayedAdd将两个数字映射到一个函数x,该函数将任意签名的函数映射到与其自身相同的签名的函数.

那么谁会做这样的事呢?为什么?

想象一下以下实现x:

 //
 // x
 // Collects functions of unspecified (possibly implicit) signatures for later execution.
 // Illustrative purpose only, do not use in production code.
 //
 // Assumes 
 function x ( fn ) {
     var fn_current;

     if (this.deferred === undefined) {
         this.deferred = [];
     }

     if (fn === undefined) {
         // apply functions
         while ( this.deferred.length > 0 ) {
             fn_current = this.deferred.shift();
             this.accumulator = fn_current(this.accumulator);
         }
         return this.accumulator;
     }

     else {
         this.deferred.push ( fn );
     }

     return this;
 }
Run Code Online (Sandbox Code Playgroud)

delayedAdd实际返回以下类型的对象的函数一起...:

 function delayedAdd ( a1, a2) {
     return x ( function () { a1 + a2; } );
 }
Run Code Online (Sandbox Code Playgroud)

...你将有效地注册一系列函数,以便在稍后的某个时间点执行(例如,在对某个事件的回调中).

备注和提醒

  • JS函数是JS对象
  • 注册函数的签名实际上可以是任意的.考虑到它们的统一只是为了使这种说明更简单(好吧......).

警告

我不知道概述的代码是否是node.js所做的(但它可能是...... ;-))


dev*_*ept 6

公平地说,这种模式可以是链接或currying(或部分应用).取决于它的实施方式.请注意,这是一个理论上的答案,可以提供有关模式的更多信息,而不是您的具体用例.

链接

这里没什么特别的,因为我们可以返回一个将再次调用的函数.javascript中的函数是一等公民

function delayedAdd(x, y) {
    // In here work with x and y
    return function(fn) {
        // In here work with x, y and fn
        return function(fn2) {
            //Continue returning functions so long as you want the chain to work
        }    
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我觉得不可读.有一个更好的选择.

function delayedAdd(x, y) {
    // In here work with x and y
    return {
        then: function(fn) {
        // In here work with x, y and fn
            return {
                then: function(fn2) {
                //Continue returning functions so long as you want the chain to work
                }
            }    
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这会改变调用函数的方式

delayedAdd(..)(..)(..); // 25 
Run Code Online (Sandbox Code Playgroud)

变成了

delayedAdd().then().then()
Run Code Online (Sandbox Code Playgroud)

当您传递多个回调函数时,不仅可以更具可读性,还可以区分下一个称为currying的模式.

哗众取宠

该术语出现在数学家Haskell Curry之后.定义是这样的

在数学和计算机科学中,currying是一种将函数的​​评估转换为评估函数序列的技术,该函数采用多个参数(或参数元组),每个函数都有一个参数(部分应用).它由MosesSchönfinkel介绍,后来由Haskell Curry开发.

基本上它所做的是获取几个参数并与subsecuents合并并将它们应用于第一个参数中传递的原始函数.

这是从Stefanv的Javascript模式中获取的这个函数的通用实现.

{编辑}

我将我之前版本的函数更改为包含部分应用程序的函数,以便做出更好的示例.在这个版本中,你必须调用没有参数的函数来获取返回的值,否则你将获得另一个部分应用的函数作为结果.这是一个非常基本的例子,在这篇文章中可以找到更完整的例子.

function schonfinkelize(fn) {
    var slice = Array.prototype.slice,
    stored_args = [],
    partial = function () {
        if (arguments.length === 0){
            return fn.apply(null, stored_args);
        } else  {
            stored_args = stored_args.concat(slice.call(arguments));
            return partial;
        }
    };
    return partial;
}
Run Code Online (Sandbox Code Playgroud)

这是此功能的应用结果

 function add(a, b, c, d, e) {
     return a + b + c + d + e;
 }
 schonfinkelize(add)(1, 2, 3)(5, 5)(); ==> 16
Run Code Online (Sandbox Code Playgroud)

请注意,add(或者在你的情况下为delayedAdd)可以作为curying函数实现,从而导致你的例子模式给你这个

delayedAdd(..)(..)(..); // 16
Run Code Online (Sandbox Code Playgroud)

摘要

仅仅通过查看调用函数的方式,您无法得出关于模式的结论.仅仅因为你可以一个接一个地调用它并不意味着链接.它可能是另一种模式.这取决于功能的实现.


paw*_*wel 5

这里所有优秀的答案,特别是@mhlz和@Leo,我想谈谈你提到的链接部分.Leo的例子显示了调用函数的想法,foo()()()但仅适用于固定数量的回调.这是尝试无限链接的尝试:

delayedAdd = function da(a, b){
// a function was passed: call it on the result
if( typeof a == "function" ){
     this.result = a( this.result )
}
else {
     // the initial call with two numbers, no additional checks for clarity.
     this.result = a + b;   
}
// return this very function
return da;
};
Run Code Online (Sandbox Code Playgroud)

现在,您可以()在第一次调用后链接任意数量的函数:

// define some functions:
var square = function( number ){ return number * number; }
var add10 = function( number ){ return number + 10; }
var times2 = function( number ){ return number * 2; }
var whatIs = function( number ){ console.log( number ); return number; }

// chain them all!
delayedAdd(2, 3)(square)(whatIs)(add10)(whatIs)(times2)(whatIs);
// logs 23, 35 and 70 in the console.
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/rm9nkjt8/3/