如何在小函数之间传递数据——通过闭包还是通过对象的属性

How should I pass data between small functions - via closures or via properties of an object?

本文关键字:闭包 属性 对象 数据 函数 之间      更新时间:2023-09-26

我有一个复杂的业务操作,例如删除一个用户帐户。它包含多个相连的步骤,并且必须跟踪步骤之间的某些状态。写这个action的更好的方法是什么?

我看到更多像下面这样的functional方法。

function someAction(someParam, anotherParam, callback) {
    async.waterfall([
            step1,
            step2,
            step3,
            step4
            ],callback
    );
    function step1(p,cb){/**use someParam and anotherParam here via closure*/}
    function step2(p,cb){/**...*/}
    function step3(p,cb){/**...*/}
    function step4(p,cb){/**...*/}
};
someAction('value', 1241, (err)=>{/**...*/});

我不喜欢这种方法的地方是,所有的东西都是在一个函数的范围内定义的(这里是someAction)。

我发现一个更object-oriented的方式是更容易读。state和stepX函数并不是真正私有的——有时它便于测试。

function SomeAction(someParam, anotherParam){
    //private state
    this._someParam = someParam;
    this._anotherParam = anotherParam;
};
SomeAction.prototype._step1 = function(p, cb){
    //use this._someParam and this._anotherParam
};
SomeAction.prototype._step2 = function(p, cb){
    //use this._someParam and this._anotherParam
};
SomeAction.prototype._step3 = function(p, cb){
    //use this._someParam and this._anotherParam
};
SomeAction.prototype._step4 = function(p, cb){
    //use this._someParam and this._anotherParam
};
//public api
SomeAction.prototype.execute = function(callback) {
    async.waterfall([
                this._step1,
                this._step2,
                this._step3,
                this._step4
            ],callback
    )
};
new SomeAction('value', 1241).execute((err)=>{/**...*/})

它们之间有性能差异吗?在Node.js中推荐的方法是什么?这是真的,每次我调用someAction函数的方法-所有的stepX函数必须从头开始定义吗?

您可以创建您的阶跃函数的简化版本,并将它们粘贴到瀑布中。

function step1(arg1, arg2, cb){
  // Fuction body...
}
// other steps would be defined here...
function step4(arg1, cb){
  // fuction body...
}
curried_step1 = step1.bind(undefined, 'junk', 'garb');
// Other steps curried here...
curried_step4 = step4.bind(undefined, 'something');
async.waterfall([
            curried_step1,
            curried_step2,
            curried_step3,
            curried_step4
        ],callback
);

另一种方法是将数据和状态打包到一个对象中(代替真正的单子),并使用该对象传递所需的内容。

你可以使用Promises,它看起来像这样:

var Promise = require('promise');
//** Execute Program **//
main();
/**
 * Do your async logic flow here
 */
function main() {
  step1()
    .then(step2) //Wait for step1 to finish, and pass the response directly to step2
    .then(function(res) {
      // Do something with res (the return from step2)
      // Async execute step3 & step4
      var promises = [
        step3(),
        step4()
      ];
      // Wait for step3 & step4 to finish:
      Promise.all([promises[0], promises[1]]).then(function(values) {
        console.log(values[0]); //Value from step3
        console.log(values[1]); //Value from step4
      }).catch(function(e) {
        console.log(e); //Reject or thrown errors from steps3 or 4
      });
    }).catch(function(e) {
      console.log(e); //Reject or thrown errors from steps1 or 2
    });
}
function step1() {
  return new Promise(resolve, reject) {
    //some async task here
    resolve('step1'); 
    //reject('step1'); //Or trigger a .catch (i.e. this function failed)
  });
}
function step2() {
  return new Promise(resolve, reject) {
    //some async task here
    resolve('step2'); 
    //reject('step2'); //Or trigger a .catch (i.e. this function failed)
  });
}
function step3() {
  return new Promise(resolve, reject) {
    //some async task here
    resolve('step3'); 
    //reject('step3'); //Or trigger a .catch (i.e. this function failed)
  });
}
function step4() {
  return new Promise(resolve, reject) {
    //some async task here
    resolve('step4'); 
    //reject('step4'); //Or trigger a .catch (i.e. this function failed)
  });
}

这不是一个完全的答案,但回答了你的间接问题:

问题是我应该如何在这些小函数之间传递数据——通过闭包还是通过对象的属性。

还有第三种方法。如果您熟悉OO设计,那么您可能熟悉命令模式的概念。也就是说,你需要动态地构造一个函数,但这是不可能的,所以你可以用一个方法创建一个对象,然后你可以根据对象的属性自定义。

在函数式编程中,这种设计模式相当于函数工厂模式。基本上就是编写一个函数来生成另一个函数。

因此,您希望将someParamanotherParam传递给异步函数,但您希望能够在someAction函数之外编写该函数。你可以这样做:

function someAction (someParam, anotherParam, callback) {
    async.waterfall([
        make_step1(someParam,anotherParam),
        make_step2(someParam,anotherParam)
        /* ... */
        ],callback
    );
}
function make_step1 (someParam, anotherParam) {
    return function (p, cb) {
        // use someParam and anotherParam here
    }
}
function make_step2 (someParam, anotherParam) {
    return function (p, cb) {
        // use someParam and anotherParam here
    }
}
// ...

这消除了您对函数式代码提出的主要异议:您不再需要在someAction()中定义所有的步骤函数,这使得它看起来更像OO代码。

这仍然会在每次调用someAction()时创建所有步骤函数的新实例(只是现在从make函数返回)。但是解释器不需要再次编译这些函数。相反,只创建一个新的闭包(将闭包看作是与程序堆栈断开链接的冻结堆栈帧)。