Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

await/async how to handle unresolved promises

How do you handle promises that do not resolve?

Example:

class Utils {
    static async thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(number: number) {
        return new Promise((resolve, reject) => {
            if(number === 2) {
                resolve('ok')
            }
        })
    }
}

console.log(await Utils.thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(2))
// this will print "ok" because 2 is passed and the promise is resolved

console.log(await Utils.thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(5))
// this will crash the program silently 

uncaughtException and unhandledRejection return nothing when the promise is unresolved. Adding a try/catch around the await doesn't work (no errors). Finally, the only thing that works is using Promise.then instead of await.

Problem is the code base is riddled with async/await and Promises that sometimes resolve (depending on conditions)

Question: Is there a typescript flag I can add to detect a missing resolve/reject? or maybe an automated way to transpile all the async/await to use Promise.then?

When using a debugger, the program stops after the Promise and it is difficult to find which function/promise has a missing resolve/reject.

Rewriting all the async/await calls to use Promise.then is my last resort.

like image 549
hbt Avatar asked Dec 22 '22 19:12

hbt


1 Answers

If you have promises that occasionally don't resolve or reject and that's not the way they are supposed to work (which it usually isn't), then you just have to fix that. There really is no work-around. The proper fix is to get down to the lowest level and fix the code so it reliably resolves or rejects every time.


This is not the proper fix, but implementing a timeout wrapper could help with debugging giving you a log message with some semblance of a stack trace for a timed out promise:

function rejectT(t) {
    // create potential error here for better opportunity at stack trace
    let e = new Error("Promise timed out");
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(e);
            reject(e);
        }, t);
    });
}

function timeout(p, t = 5000) {
    return Promise.race([p, rejectT(t)]);
}

You can then wrap any promise such that instead of:

fn().then(...).catch(...)

You can use:

timeout(fn()).then(...).catch(...);

Or, if you want to set a custom timeout value:

timeout(fn(), 1000).then(...).catch(...);

Again, this is debugging code to help find the culprits that need fixing and to help test fixes, not to bandaid your code.

Rewriting all the async/await calls to use Promise.then is my last resort.

I don't see how this is going to help at all. If await never finishes, neither will promise.then(). They are exactly the same in that regard. If the promise never resolves or rejects, then the .then() handler will never get called either.

Problem is the code base is riddled with async/await and Promises that sometimes resolve (depending on conditions)

There's no shortcut here other than methodical code review to find suspect code that has code paths that may never resolve or reject and then building unit tests to test every function that returns a promise in a variety of conditions.

One likely source of code that never resolves or rejects are some of the promise anti-patterns. The precise reason some of them are anti-patterns is because they can be very easy to mess up. Here are a few references that might spike your sensitivity to suspect code:

Promise Anti-Patterns

Common Promise Anti-Patterns and How to Avoid Them

ES6 Promises: Patterns and Anti-Patterns

like image 62
jfriend00 Avatar answered Dec 29 '22 06:12

jfriend00