[英]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.