How to rewrite forEach to use Promises to stop "freezing" browsers?

时间:2015-11-12 11:47:36

标签: javascript ecmascript-6 es6-promise

I have a function, that looks like this.

 function () {
      longArray.forEach( element => doSomethingResourceIntensive(element))
 }

Because the array is long and the function is a little resource intensive, it freezes the browser.

Now I want to rewrite it using Promises, so it does the same thing, just not freezing the browser, and I want the solution to be elegant and "ES6-y"; ideally, the function would return Promise when all the iterations finished.

I found this question, where it's dealt with using setTimeout, but it seems a little "un-ES6-y", and it doesn't return a Promise.

I cannot do

 function () {
      return Promise.all(longArray.map( element => 
          Promise.resolve().then(() => doSomethingResourceIntensive(element))
      )
 }

because I need to run the promises in succession and I am not sure if it would happen there.

2 个答案:

答案 0 :(得分:4)

If you need to run promises in succession, you want to chain the .then calls. You normally do that with .reduce():

function () {
    return longArray.reduce((promise, el) => 
        promise.then(() => doSomethingResourceIntensive(el)),
      Promise.resolve()); // Start with a clean promise!
}

Also, depending on the type of job you do, you may want to have a look into Web Workers, which are executed in another thread and thus don't block the page.

答案 1 :(得分:1)

您引用的答案是正确的,您需要setTimeout。单独使用promise链不会有帮助,因为.then链在微任务队列上执行,在大多数浏览器中,在当前的run-to-completion的尾部完全清空。换句话说,他们仍会冻结东西。

如果你想要ES6-y,我依靠这个值得信赖的帮手:

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));

然后我可以这样做:

longArray.reduce((p, i) => p.then(() => doIntensiveStuff(i)).then(() => wait(5)),
                 Promise.resolve());

除非你当然可以使用工人。