Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 424
Karel Bílek Avatar asked Nov 12 '15 11:11

Karel Bílek


2 Answers

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.

like image 120
Madara's Ghost Avatar answered Nov 01 '22 19:11

Madara's Ghost


The answer you reference is right, you need setTimeout. Using a promise-chain alone wont help because .then chains execute on a microtask queue, which in most browsers are emptied completely at the tail of the current run-to-completion. In other words, they'll still freeze things.

If you want something ES6-y, I rely on this trusty helper:

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

Then I can do this:

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

Unless you can use workers of course.

like image 20
jib Avatar answered Nov 01 '22 19:11

jib