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

时间:2016-02-05 15:37:20

标签: javascript asynchronous callback

我有一个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之前结束。这显然不会出现在当前代码中,因为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_then函数完成后才会调用f

但有时,传递的ffor_users不是异步的,上面的实现就足够了。有没有办法编写一个通用for_users实现,它既可以用于异步和同步f函数?

3 个答案:

答案 0 :(得分:1)

你可以在f函数中添加下一个延续回调,如下所示:

Play App

然后您可以使用以下方法调用此函数:

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

答案 1 :(得分:1)

这对你有用: -

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

答案 2 :(得分:0)

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函数。我讨论了这些功能,但这并不是必需的。这只是一种便利。