繁体   English   中英

在定制的for-each循环中链接回调,同时支持同步和异步功能

[英]Chaining callbacks in a custom-made for-each loop, supporting both synchronous and asynchronous functions

我有一个for_users函数,该函数从Web服务获取用户数组,在接收到的数组上执行传递的函数f ,然后调用延续f_then回调。

// Execute f on every user, then f_then.
function for_users(f, f_then)
{
    // Get all users from the database, in user_array
    db.get_all_users(function(user_array)
    {
        // Execute f on every user
        user_array.forEach(f);

        // Call continuation callback
        f_then();
    });
}

在调用for_users ,将异步函数作为f参数传递,我希望所有f回调在调用f_then之前f_then 这显然在当前代码中没有发生,因为user_array.forEach(f)在开始下一次迭代之前不会等待f完成。

这是一个有问题的情况的示例:

function example_usage()
{
    var temp_credentials = [];

    for_users(function(user)
    {
        // Get credentials is an asynchronous function that will
        // call the passed callback after getting the credential from
        // the database
        database.get_credentials(user.ID, function(credential)
        {
            // ...
        });
    }, function()
    {
        // This do_something call is executed before all the callbacks
        // have finished (potentially)

        // temp_credentials could be empty here!
        do_something(temp_credentials);
    });
}

如何实现for_users ,以便如果f是一个异步函数,则仅在所有f函数都完成f_then调用f_then

但是,有时,传递给for_users f不是异步的,上述实现就足够了。 有没有一种方法可以编写适用于异步和同步f函数的通用for_users实现?

您可以向f函数添加下一个延续回调,如下所示:

function for_users(f, f_then) {
    // Get all users from the database, in user_array
    db.get_all_users(function(user_array) {
        // Execute f on every user
        (function recur(next) {
            var user = user_array.shift();
            if (user) {
                f(user, function() {
                    recur(next);
                });
            } else {
                // Call continuation callback
                next();
            }
        })(f_then);
    });
}

然后您将可以使用以下命令调用此函数:

for_users(function(user, next) {
    // Get credentials is an asynchronous function that will
    // call the passed callback after getting the credential from
    // the database
    database.get_credentials(user.ID, function(credential) {
        next();
    });
}, function() {
    // This do_something call is executed before all the callbacks
    // have finished (potentially)

    // temp_credentials could be empty here!
    do_something(temp_credentials);
});

这应该为您工作:

function for_users(f, f_then) {

  db.get_all_users(function(user_array) {
      var promises = [];

      user_array.forEach(function(user) {
        promises.push(new Promise(function(resolve, reject) {
          f(user);
          resolve();
        }));
      });

      if (f_then)
        Promise.all(promises).then(f_then);
      else
        Promise.all(promises);
    }
  });
}

简单测试如下:

 function for_users(f, f_then) { var users = [{ID: 1}, {ID: 2}, {ID: 3}]; var promises = []; users.forEach(function(user) { var promise = new Promise(function(resolve, reject) { f(user); resolve(); }); promises.push(promise); }) if (f_then) Promise.all(promises).then(f_then); else Promise.all(promises) } for_users(function(user) { console.log(user.ID); }, function() { console.log('finshed') }) 

var getCredentials = function(step){
    return function(user){
        database.get_credentials(user.ID, function(credential) {
            step(credential);
        });
    };
};

var allFinish = function(f){
    return function(step) {
        return function(arr){
            var finished = 0;
            var values = new Array(arr.length);
            if(arr.length){
                arr.forEach(function(el, i){
                    f(function(value){
                        if(finished === arr.length){
                            step(values);
                        } else {
                            values[i] = value;
                            finished++;
                        }
                    })(el);
                });
            } else {
                step(values);
            }
        };
    };  
};

var forEachUser = function(doSomething){
    db.get_all_users(allFinish(getCredentials)(doSomething));
}

然后,您可以简单地执行以下操作:

forEachUser(function(tempCredentials){
    //tempCredentials === allCredentials
});

可能有更好的方法来处理allFinish插入数组的值的顺序。 allFinish工作方式是采取一个执行步骤的函数,然后使用一个step函数进行调用,该函数将在所有调用完成后调用另一个step函数。 我使用了函数,但这并不是必需的。 这只是一个方便。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM