2016-02-05 41 views
1

我有一个for_users函数,它从Web服务获取用户数组,在接收的数组上执行传递函数f,然后调用继续f_then回调。在定制for-each循环中链接回调,同时支持同步和异步函数

// 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函数工作?

回答

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

1

你可以一个延续回调添加到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); 
}); 
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函数。我喜欢这些功能,但它并不是真的有必要。这只是一个方便。