如何使用Q.js将Array.forEach转换为异步?

3

我需要动态地为数组中的所有项执行一个函数,但是 Array.forEach 按顺序执行,我需要异步执行。

items.forEach(function(item) {
    doSomething(item);
});

我尝试了这个:
var promises = [];

items.forEach(function(item) {
    var promise = function() {
        return Q.fcall(function() {
            doSomething(item);
        });
    };
    promises.push(promise());
});

Q.all(promises).then(function () {
    otherFunction(datacontext.mainList); //use datacontext.mainList filled.
});

但是执行总是按顺序进行,我需要并行执行。

doSomething(item) 方法:

function doSomething(item) {
        var children = getChildren(item); //get data from local with manager.executeQueryLocally
        var total = getTotal(children); //simple calculations
        datacontext.mainList.push({
            name: item.firstName() + ' ' + item.lastName(),
            total: total
        });
    }

请帮助我。


请查阅WebWorkers,JavaScript是单线程的。 - OneOfOne
你可以将items.forEach放在setTimeout中。setTimeout(function () {(你的代码在这里)}, 1) - Damian Krawczyk
你试图使用错误的工具来处理问题。Promise 不是将同步代码转换为异步代码的工具,而是用于驯服异步回调链复杂性的一种手段。 - Scott Sauyet
这不是代码审查,但这是一个很好的地方来使用map函数:var promises = items.map(function() {/*创建promise*/ return promise;}); - megawac
1
你能澄清一下你所说的“异步”是什么意思吗?“异步”意味着doSomething不会在一个事件中完成。如果它在一个事件中完成,它将始终以“串行”的方式执行,但不一定按顺序执行。如果doSomething返回一个Promise或有一个回调,并且不会在单个事件中完成,那么就会打开“并行”的可能性,其中多个作业的事件会交错。无论哪种方式,我都可以提供提示。 - Kris Kowal
方法doSomething非常懒惰,我需要在“并行”中执行该方法。如何并行执行此方法? - kuskunko
2个回答

21

假设 doSomething 是一个异步操作,意味着它将不得不交还控制权给事件循环,并等待另一个事件。如果 doSomething 是同步的,则将其组合为异步没有任何好处。

在复合异步作业的范围内,存在串行和并行变体。串行模型会导致作业(n+1)在作业(n)完成后才开始。并行模型会在最初启动所有作业,并在所有作业完成时结束。在这两个方面,我可以给你一些提示。

在并行执行中,您可以使用数组映射和 Q.all,假设doSomething接受jobs的值并返回一个 Promise:

return Q.all(jobs.map(doSomething))

要按顺序执行任务,请使用数组reduce。

return jobs.reduce(function (previous, job) {
    return previous.then(function () {
        return doSomething(job);
    });
}, Q());

如果你想串行执行作业,但只有在第一个作业的结果确定后才继续执行下一个作业,则可以使用 reduceRight 来组合一个函数。

return jobs.reduceRight(function (next, job) {
    return function (previous) {
        return doSomething(job).then(function (result) {
            if (result.isGood(previous)) return result;
            return next(result);
        });
    });
}, function fallthrough(previous) {
    throw new Error("No result was satisfactory");
})();
如果您有包含需要按顺序执行的“函数”的数组,并将上一个函数的输出传递给下一个函数的输入,您可以使用reduce更加简洁地实现。
return functions.reduce(Q.when, Q());

Q 的自述文件中的教程涵盖了更多情况,我被告知非常有帮助 https://github.com/kriskowal/q#tutorial


0

看起来你正在迭代时立即执行创建的 Promise,因为你将它推入了数组中。尝试更改...

promises.push(promise());

关于...

promises.push(promise);

1
你的 doSomething(item) 方法是同步方法吗?Q 不会将你的同步方法转换为异步方法。你的 doSomething 函数需要是异步的。 - xspydr
是的,可以帮我吗?如何将 doSomething(item) 转换为异步方法? - kuskunko
我已经编辑了我的问题并添加了 doSomething(item) 方法。 - kuskunko
1
你需要重写doSomething方法来使用回调函数。或者,你可以使用async(https://github.com/caolan/async#asyncjs)库将同步代码转换为异步代码。此外,如果你的代码中不需要Promise,则应该摆脱Q... - xspydr
请查看此问题:Parallel.js在IE中使用Blob存在问题 - kuskunko
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接