3

I have a few hundreds of things to render in parallel in an html5 canvas. These are drawn in parallel in a Promise.all call. Now, I would like to know which of these promise is the last to be resolved.


// get a promise that will resolve in between 0 and 5 seconds.
function resolveAfterSomeTime(): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}

const myPromises = [];
for (let i = 0; i < 100; i++) {
    myPromises.push(resolveAfterSomeTime);
}

Promise.all(myPromises).then(() => {
    // find out which promise was the last to resolve.
})

In my case, I have multiple classes with each a render() function. Some of these are heavier than others, but I want to know which ones.

I have something along these lines, and I would like to know which promise is the slowest to resolve, so that I can optimise it.

ecstrema
  • 543
  • 1
  • 5
  • 20

3 Answers3

4

The best way I can think of is to use a counter indicating the number of promises that have resolved so far:

function resolveAfterSomeTime() {
  return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}

const myPromises = [];
let resolveCount = 0;
for (let i = 0; i < 100; i++) {
  myPromises.push(
    resolveAfterSomeTime()
      .then(() => {
        resolveCount++;
        if (resolveCount === 100) {
          console.log('all resolved');
          console.log('array item', i, 'took longest');
        }
      })
  );
}
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
4

Here's a way where each promise sets the value of lastPromiseToResolve after resolving. The last promise to resolve would set it last.


// get a promise that will resolve in between 0 and 5 seconds.
function resolveAfterSomeTime(): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}

let lastPromiseToResolve = null
const myPromises = [];
for (let i = 0; i < 100; i++) {
    const promise = resolveAfterSomeTime()
    myPromises.push(promise.then(() => {
        lastPromiseToResolve = promise // all the promises will set lastPromiseToResolve
    }));
}

Promise.all(myPromises).then(() => {
  console.log(lastPromiseToResolve) // this would be the last promise to resolve
})
richytong
  • 2,387
  • 1
  • 10
  • 21
3

You could time each promise. You could even assign an identifier to each one if you want to know specifically which is resolving. The timePromise function below takes an id and a function that returns a promise, times that promise, and logs the result. It doesn't change the result of the promises, so you can use myPromises as you normally would.

function resolveAfterSomeTime() {
    return new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
}

// f is a function that returns a promise
function timePromise(id, f) {
  const start = Date.now()
  
  return f()
    .then(x => {
      const stop = Date.now()
      
      console.log({id, start, stop, duration: (stop - start)})
      return x
    })
}

const myPromises = [];
for (let i = 0; i < 100; i++) {
    myPromises.push(timePromise(i, resolveAfterSomeTime));
}

Promise.all(myPromises).then(() => {
    // find out which promise was the last to resolve.
})

I'm not sure how you're creating your array of promises in your actual code, so it might not be straightforward to wrap each promise in a function that returns it. But you could likely adapt this to work with your situation.

If you aren't concerned with knowing exactly how long each takes, you could just have timePromise take a promise that's already started, and time from when timePromise is called to when it resolves. This wouldn't be as accurate, but would still give you a general idea, especially if one or a few promises are taking much longer than others.

Something like this:

function timePromise(id, p) {
  const start = Date.now()
  
  return p
    .then(x => {
      const stop = Date.now()
      
      console.log({id, start, stop, duration: (stop - start)})
      return x
    })
}

const myPromises = [];
for (let i = 0; i < 100; i++) {
    myPromises.push(timePromise(i, resolveAfterSomeTime()));
}
Cully
  • 6,427
  • 4
  • 36
  • 58
  • Interesting, but since the promises all run in parallel, it doesn't matter how long it takes. all that matters is how long the slowest takes. – ecstrema Apr 18 '21 at 00:16
  • 1
    You said you want to optimize the slowest one. What if they all take about the same amount of time? Or what if more than one promise is taking a long time? If all you know is the slowest promise, you could waste time optimizing it even though it takes about the same amount of time as all the others, or you might miss that you need to optimize more than one. This way you'll have an idea of what exactly is contributing to your long render time. – Cully Apr 18 '21 at 00:18
  • 1
    And if you want to know which finishes last, just look at the last log message :) – Cully Apr 18 '21 at 00:20
  • 2
    For example, if you have 5 promises and these are their runtimes in milliseconds: [10, 10, 10, 4900, 5000]. You would realistically want to optimize the last two. Or if these are their runtimes: [100, 101, 102, 103, 104]. You wouldn't really gain much by optimizing the last one, since all the others run about as quickly/slowly. – Cully Apr 18 '21 at 00:23
  • 1
    @MarcheRemi Oops, forgot to tag you in ^^^ – Cully Apr 18 '21 at 00:30
  • These are all noteworthy comments. Thank you. – ecstrema Apr 18 '21 at 02:06