user3909192
user3909192

Reputation:

How can I track the number of unfulfilled promises in a NodeJS application?

I frequently use a pattern in NodeJS & JavaScript where I combine a timeout with another promise using Promise.race, like this...

let timer
let p1 = x => {
  return new Promise((resolve, reject) => {
    bSomeFunctionCall(x) ? resolve() : reject()
  }).finally(() => { console.log('p1 done') })
}
let p2 = ms => {
  return new Promise((resolve, reject) => {
    timer = setTimeout(() => {
      reject('timeout')
    }, ms)
  }).finally(() => { console.log('timeout done') })
}
let p3 = Promise.race([p1(v1), p2(3000)])
await p3.finally(() => {
  console.log('race done')
  clearTimeout(timer)
  console.log('done')
})

EDIT: Typo, 'timer' is a variable in scope for clearing.

However, I never see the p2.finally() message unless it times out & rejects, which makes me wonder if every time I use this pattern I create an unfulfilled promise floating around out there... potentially thousands of them.

I'm wondering if clearTimeout() in p3 causes the promise to resolve and since resolve() isn't defined, it just quietly settles, but shouldn't I see p2's finally() call?

I don't understand how p2 can settle and not call it's finally() method.

How can I debug this to convince myself I'm not making a mess under the hood? I was hoping there's a way to examine the number of outstanding promises in a debug mode...

Upvotes: 0

Views: 802

Answers (1)

Marcos Casagrande
Marcos Casagrande

Reputation: 40404

.finally(() => { console.log('timeout done') }) always gets called, unless you never reject that Promise, which happens because you're clearing the timeout apparently when Promise.race is settled.

In your code you assign the timeout id to timer but then issue clearTimeout with timeout variable, which isn't defined in that script. So I'm assuming you're clearing the p2 promise timeout, and since p1 finishes first, when you clear the timer p2 never gets settled.

So don't use clearTimeout since doesn't make sense if you're using Promise.race, and finally will always be called on p2


Just for debugging purposes, you can override Promise constructor to keep track of all promises.

Promise = class extends Promise {
    static list = []
    constructor(...args) {
        let p = super(...args)
        Promise.list.push(p)
        return p;
    }
};

// Promise.list (will contain all promises, filter by `pending`

You can also check this answer: View all pending promises in javascript

Upvotes: 0

Related Questions