1

I'm defining a function that requires an asynchronous function as a parameter:

async function handle(def: Promise<string>) {
    // ...
    const data = await def;
    console.log(`data equals: ${data}`)
}

I can succesfully execute this by passing a promise.

handle(new Promise(async (res, rej) => {
    const data = await Promise.resolve("some data")
    if (data == "invalid")
        return rej("data is invalid")
    res(data)
}))

I need the inner-function to be async, since I need to perform awaits inside. However, I dislike the async in the promise and read online that its considered an anti-pattern.

I thought it was a better idea to get rid of the promise and use a basic async function:

handle(async () => {
    const data = await Promise.resolve("some data")
    if (data == "invalid")
        throw "data is invalid"
    return data
})

But the TS compiler raises the error:

Argument of type '() => Promise<string>' is not assignable to parameter of type 'Promise<string>'. ts(2345)

I thought that Promises and async functions are somewhat interchangable. I read that async functions always return a promise. Apperantly I am not interpreting this correctly, but I'm unsure what the error is telling me.

I hope the problem I am sketching is clear. It would be greatly appreciated if someone could clarify the error or give suggestions on how to implement this in the desired way. Big thanks!

sander
  • 73
  • 1
  • 1
  • 9
  • What **exactly** should `def` be? A `Promise` or a function `() => Promise`? Your code says the former but your description says the latter – Phil Feb 24 '22 at 02:07

2 Answers2

3

your function signature seems to be incorrect. you are defining the parameter to be a Promise<string>, while you're really willing it to be () => Promise<string>.

async function handle(def: () => Promise<string>) {
    // ...
    const data = await def();
    console.log(`data equals: ${data}`)
}
Yone
  • 867
  • 1
  • 7
  • 22
  • 1
    I didn't get the impression that OP wants to change the signature for `handle()`. Also, you need to call `def()` in your answer – Phil Feb 24 '22 at 02:03
  • @Phil, well, the parameter is described as `an asynchronous function` above. thanks for the notice – Yone Feb 24 '22 at 02:04
  • Not in OP's code it's not... `def: Promise` – Phil Feb 24 '22 at 02:05
  • i'm more about the text description above the code snippet – Yone Feb 24 '22 at 02:06
  • 1
    This is exactly what I was looking for! I had no idea the solution would be so evident. The TS error with `() =>` didn't look familiar and thought the problem was rooted deeper. Many thanks. – sander Feb 24 '22 at 02:15
0

In general, the time to use the Promise constructor in a situation like this is right when you call a callback-based function that you want to promisify. But here, it doesn't look like there's anything to promisify, so the Promise constructor doesn't really help.

If

I need the inner-function to be async, since I need to perform awaits inside.

You can have an immediately invoked async IIFE - yes, using an async callback for the Promise constructor wouldn't be safe, because it wouldn't handle errors.

handle((async () => {
    const data = await Promise.resolve("some data")
    if (data == "invalid")
        throw "data is invalid";
    return data
})())

The other option is to use .then - .then is interchangeable with await in many situations:

handle(
  Promise.resolve("some data")
    .then((data) => {
        if (data == "invalid")
            throw "data is invalid";
        return data;
    })
);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thanks for your contribution. I have to get more familiar with the .then method, but your example looks 'considering-worthy' to implement. – sander Feb 24 '22 at 02:18