了解延期执行链的语法

我正在学习JavaScript,真正学习JavaScript。 我来自PHP背景,所以一些JavaScript概念对我来说还是新的,特别是asynchronous编程。 这个问题可能已经被回答了很多次,但是我一直没有find答案。 这可能是因为除了举个例子之外,我甚至不知道怎么去问这个问题。 所以这里是:

当从npm使用延迟包时,我看到下面的例子:

delayedAdd(2, 3)(function (result) { return result * result })(function (result) { console.log(result); // 25 }); 

他们把这称为链接,它实际上工作,因为我目前正在使用这个代码来检查一个承诺是否解决或被拒绝。 尽pipe他们称之为链接,但它让我想起了像Swift那样的闭包。

我不明白这是什么types的链接,因为我们有一个函数调用,然后紧接在括号后面的一个匿名函数。

所以我想我有两个问题。

  1. 这是什么模式?
  2. 它是如何工作的? 这可能是一个加载的问题,但我想知道如何做的事情,所以当有人问我这个问题时,我可以给他们一个详细的解释。

这是delayedAdd函数:

 var delayedAdd = delay(function (a, b) { return a + b; }, 100); 

它使用以下function:

 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; }; }; 

这实际上很容易理解。 让我们来看看在评估expression式时发生了什么:

首先delayedAdd(2, 3)函数。 它做了一些东西,然后返回。 这个“魔术”就是它的一个function返回值。 更准确地说,这是一个函数,至less有一个参数(我会回到那个)。

现在我们将delayedAdd(2, 3)为一个函数,我们到达代码的下一部分,即delayedAdd(2, 3)括号。 打开和closures括号当然是函数调用。 所以我们要调用delayedAdd(2, 3)刚刚返回的函数,我们将传递一个参数,这个参数是下一个定义的:

这个说法还有另一个function(就像你在例子中看到的那样)。 这个函数也需要一个参数(计算的结果)并返回它自己的乘积。

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

所以总结一下,我们通过将代码传递给delayedAdd(2, 3)返回的函数来build立一个函数链。 这些函数将返回其他函数,我们可以再次传递我们的函数。

我希望这可以使它的工作方式有些清晰,如果不能随意问更多的话。

mhlz的回答非常明确。 作为一个补充,在这里我写一个delayedAdd ,以便更好地理解这个过程

 function delayedAdd(a, b) { var sum = a + b return function(f1) { var result1 = f1(sum) return function(f2) { f2(result1) } } } 

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

 function (result) { return result * result } 

f2是:

 function (result) { console.log(result) } 

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

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

 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 ) 

通用设置

当然,JS是弱types语言,所以列出的签名是通过猜测从代码片段派生的。 除了检查源代码之外,没有办法知道代码是否实际上做了上面build议的内容。

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

 delayedAdd: number x number -> fn (( fn T -> void ) -> ( fn T -> void )) 

这意味着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; } 

再加上一个函数delayedAdd ,实际上返回下列types的对象…:

  function delayedAdd ( a1, a2) { return x ( function () { a1 + a2; } ); } 

…您将有效地注册一些function链,以便在稍后的某个时间点执行(例如,在某个事件的callback中)。

笔记和提醒

  • JS函数是JS对象
  • 注册函数的签名实际上可能是任意的。 考虑到他们是统一的,只是为了保持这个博览会更简单(好…)。

警告

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

公平地说,这种模式可以是链式或者咖喱(或者是部分应用)。 取决于如何实施。 请注意,这是提供有关模式的更多信息的理论答案,而不是您的具体使用情况。

链接

这里没有什么特别的,因为我们可以返回一个将被再次调用的函数。 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 } } } 

这使我看来难以理解。 有一个更好的select。

 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 } } } } } 

这改变了你的function被调用的方式

 delayedAdd(..)(..)(..); // 25 

被转化为

 delayedAdd().then().then() 

不仅在传递多个callback函数时更具可读性,而且允许区别于下一个称为currying的模式。

哗众取宠

math家哈斯克尔·库里(Haskell Curry)之后出现了这个词。 定义是这样的

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

基本上它是做了几个参数,并与subsecuents合并,并将它们应用到第一个parameter passing的原始函数。

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

{编辑}

我改变了我以前版本的function,其中包含了部分应用程序作为一个更好的例子。 在这个版本中,你必须调用没有参数的函数来得到返回的值,否则你会得到另一个部分应用的函数。 这是一个非常基本的例子,可以在这篇文章中find更完整的例子。

 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; } 

这是这个函数的应用的结果

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

请注意,添加(或在你的例子delayedAdd)可以实现为导致你的例子给你这个模式的curying函数

 delayedAdd(..)(..)(..); // 16 

概要

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

所有优秀的答案,尤其是@mhlz和@Leo,我想谈谈你提到的链接部分。 Leo的例子显示了调用像foo()()()这样的函数的想法,但只适用于固定数量的callback函数。 这是一个尝试无限制的链接:

 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; }; 

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

 // 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. 

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

如果我们从逻辑上扩展这个语法,我们可以这样做:

 var func1 = delayedAdd(2, 3); var func2 = function (result) { return result * result }; var func3 = function (result) { console.log(result); }; var res = func1(func2); // variable 'res' is of type 'function' res(func3);