DEV Community

Pedro Osternack Corrêa
Pedro Osternack Corrêa

Posted on

TIL: You can await a promise with callbacks.

I was working on some code and saw the following:

await somePromise
    .then(/* doing something with the resolved value*/)
    .catch(/* handling errors*/);
Enter fullscreen mode Exit fullscreen mode

I need to admit that it took me a while to understand what was happening there.
After that moment of surprise I went to the MDN Docs on promises and finally understood what was going on there.
My main surprise came from the habit of either using the callback style of the async/await one, but never mixing both. To be honest when async/await became official I've seldom used the callback notation again.
So a bit of context, a ton of Promise methods return a Promise. That's what enables us to chain .then() calls or have something like .then().catch().finally(). Which of these methods returns a new Promise.
Async/Await is basically a bit of syntax sugar for deal with Promises. Async functions are functions that return a Promise. Await (in a simplified way) allows us to handle a Promise being resolved in another way without needing the .then() callback.

let value = await somePromise;
// is the same as
let value;
somePromise.then((v) => value = v);
Enter fullscreen mode Exit fullscreen mode

But as I've alluded to before, the then, catch and finally methods return each a new Promise. So, since async/await is just syntax sugar for promises, we can mix and match both notations.
Here are some examples:

requestPromise
    .then((resp) => resp.data)
    .then((data) => updateState(data))
    .catch((err) => console.error(err))
    .finally(() =>  console.info("we're done here"));

// --------------------------
// Is the same as
// --------------------------

try {
    const resp = await requestPromise;
    await updateState(resp.data);
} catch (err) {
    console.error(err);
} finally {
    console.info("we're done here")
}

// --------------------------
// Or even
// --------------------------

// assuming only this method will throw an error.
// if it throws, resp will be undefined
const resp = await requestPromise
    .catch((err) => console.error(err));

if (resp) {
    await updateState(resp.data);
}

console.info("we're done here")

// --------------------------
// Or we can just await for the whole thing in a async function
// --------------------------

await requestPromise
    .then((resp) => resp.data)
    .then((data) => updateState(data))
    .catch((err) => console.error(err))
    .finally(() =>  console.info("we're done here"));
Enter fullscreen mode Exit fullscreen mode

It's an interesting pattern that honestly I never though about until now and I found it curious. I'm not 100% sure if I will mix and match the styles like this, I think it may become a bit confusing, at least while you're not used to it.
I can see the appeal of having the catch without the try block, but again, not 100% sold on it. Probably time will tell.
So, what do you think? Is this a pattern you've seen before? Is this something that you actively use? If so, in what contexts? If not would you use this mixing pattern?

Top comments (0)