Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using await within a Promise

There seems something inherently wrong with having to define a Promise's callback as asynchronous:

return new Promise(async (resolve, reject) => {   const value = await somethingAsynchronous();   if (value === something) {     return resolve('It worked!');   } else {     return reject('Nope. Try again.');   } }); 

This is apparently an antipattern and there are coding problems which can arise from it. I understand that it becomes easier to fail to catch errors here, even when placing await statements inside try/catch blocks.

My first question is, what's the best way to code something like this, when one wants to forward a Promise with different resolve/reject values? With then/catch? I.e.

return new Promise((resolve, reject) => {   somethingAsynchronous().then(value => {     if (value === something) {       return resolve('It worked!');     } else {       return reject('Nope. Try again.');     }   }); // errors would now be propagated up }); 

Or do you just take it out the Promise constructor altogether as suggested here?

async function outerFunction() {   const value = await somethingAsynchronous();   return new Promise((resolve, reject) => {     if (value === something) {       return resolve('It worked!');     } else {       return reject('Nope. Try again.');     }   }); } 

But what if you have several await statements in the outerFunction(), i.e. a linear code block calling several asynchronous functions. Would you then have to create and return a new Promise every time?

But then how do you account for code such as this?

async function outerFunction() {   if (someSynchronousCheck()) {     return 'Nope. Try again.' // another reject case   }    const value = await somethingAsynchronous();   // ... } 

I have the feeling that I'm making this more complicated than it should be. I'm trying to avoid nesting callbacks/chaining then/catch blocks without creating more problems in the future.

My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.

like image 396
Druckles Avatar asked Jul 27 '17 11:07

Druckles


People also ask

Can you use await within a promise?

The await operator is used to wait for a Promise . It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.

Can we use async in promise?

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise. Note: Even though the return value of an async function behaves as if it's wrapped in a Promise.resolve , they are not equivalent.

Should I use await in promise all?

Don't use await inside loops. Create an array of Promises and await Promise. all instead.

Can we use promise and async await together?

Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use. It makes asynchronous code look more like synchronous/procedural code, which is easier to understand.


2 Answers

You do this:

async function outerFunction() {   const value = await somethingAsynchronous();   if (value === something) {     return 'It Worked!';   }   throw Error('Nope. Try again.'); } 

Using async wraps the result of outerFunction with a Promise.

If you want that wrapping promise to resolve to something, just return it from the async function. If you want the wrapping promise to be rejected, throw an error inside the async function.

But then how do you account for code such as this?

async function outerFunction() {   if (someSynchronousCheck()) {     throw Error('Nope. Try again.');   }    const value = await somethingAsynchronous();   // ... } 
like image 52
robertklep Avatar answered Oct 11 '22 04:10

robertklep


new Promise(async (resolve, reject) => { ... }) is relatively new antipattern. It results in creating 2 promise objects instead of 1, uncaught errors that happen inside constructor cannot be caught with try..catch and result in unhandled rejection.

Considering that promise asynchronous code can be handled with async..await, current use case for Promise constructor is non-promise asynchronous code, e.g.:

new Promise(resolve => setTimeout(resolve, 1000)) 

When Promise constructor contains synchronous code or involves other promises, a promise should be constructed with async function. A drop-in replacement is async IIFE:

return (async (resolve, reject) => {   const value = await somethingAsynchronous();   if (value === something) {     return 'It worked!';   } else {     throw 'Nope. Try again.';   } })(); 

If the need for Promise constructor still presents when being used together with async, Promise constructor should be moved down in hierarchy so it won't wrap any async function.

My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.

async function isn't just a function that is executed asynchronously, it returns another promise that is supposed to be utilized - or at least handled with catch. Promise isn't supposed to utilize a promise that is returned from constructing function.

The constructor can resolve on same tick and doesn't necessarily have to be asynchronous.

Promise.resolve(1); 

is similar to

Promise(resolve => resolve(1)) 

and not to

Promise(resolve => setTimeout(() => resolve(1))) 
like image 38
Estus Flask Avatar answered Oct 11 '22 05:10

Estus Flask