Differencebetween JavaScript Promises and Async Await

What is the difference between JavaScript promises and async await?

Why is async,await needed when Promises does similar job? Does async,await solve a bigger problem?

async/await simply gives you a synchronous feel to asynchronous code. It's a very elegant form of syntactical sugar.

For simple queries and data manipulation, Promises can be simple, but if you run into scenarios where there's complex data manipulation and whatnot involved, it's easier to understand what's going on if the code simply looks as though it's synchronous (to put it another way, syntax in and of itself is a form of "incidental complexity" that async/await can get around).

If you're interested to know, you can use a library like co (alongside generators) to give the same sort of feel. Things like this have been developed to solve the problem that async/await ultimately solves (natively).

What is difference between Promise, await/async, and setTimeout?

With setTimeout you just set a time in milliseconds that the procedure should wait until the callback function inside the setTimeout call is getting called.

Meaning:

setTimeout(() => {
console.log('Hello World!');
}, 1000);

When the setTimeout is getting executed, it will wait 1000 milliseconds = 1 second until it performs the console.log(); action.

In case of error handling, you still need to handle your errors manually, but you're also able to resolve Promises in the setTimeout.
So you could tell the program that it should wait 1 second until it resolves the Promise and it can catch exceptions as you said with the .catch() on the Promise call.

TL;DR: Async methods just wait until the method is done (if you await them) and setTimeout executes a code block after a given amount of milliseconds.

Promises vs async await behaviour javascript. Concurrency mode

To answer your questions, let me first try to explain how your code is working.

Understanding how your code works

Following steps explain the execution of your code:

  1. Script execution start

  2. Call disallowConcurrency function, passing in the initCmp as an argument. cmpConcurrentFunction is assigned the return value of disallowConcurrency function

  3. Call cmpConcurrentFunction for the first time, passing in 'I am called 1 second later' as an argument. During this invocation, inprogressPromise is a resolved promise returned by Promise.resolve(). Awaiting it pauses the function execution.

  4. Call cmpConcurrentFunction for the second time, passing in 'I am called 2 seconds later' as an argument. During this second invocation, inprogressPromise is still a resolved promise returned by Promise.resolve(). Awaiting it pauses the function execution.

  5. Synchronous execution of script ends here. Event loop can now start processing the micro-task queue

  6. Function paused as a result of first invocation of cmpConcurrentFunction is resumed, calling then() method on the promise returned by Promise.resolve(). The value of the inprogressPromise is updated by assigning it a new promise returned by inprogressPromise.then(...)

  7. Function paused as a result of second invocation of cmpConcurrentFunction is resumed. From step 6, we know that inprogressPromise is now a promise returned by inprogressPromise.then(...). So, calling then() on it, we are simply creating a promise chain, adding a new then() method call at the end of the promise chain created in step 6.

    At this point, we have a chain that looks like this:

    inProgressPromise
    .then(() => fn('I am called 1 second later'))
    .then(() => fn('I am called 2 seconds later'))
  8. Callback function of the first then method is called, which in turn calls the fn argument, which is initCmp function. Calling initCmp sets up a timer that resolves the promise initCmp returns after 1 second. When the promise is resolved after 1 second, 'I am called 1 second later' is logged on the console.

  9. When the promise returned by the first call to initComp function in the callback function of first then method, is resolved, it resolves the promise returned by the first then method. This leads to the invocation of the callback function of the second then method. This again calls the initComp function, which then returns a new promise that is resolved after another 1 second.

This explains why you see 'I am called 2 seconds later' logged on the console after 2 seconds.


Now answering your questions:

But i don't understand why this keeps working if i remove the await
inprogressPromise

await inprogressPromise serves no purpose in your code other than pausing the calls to cmpConcurrentFunction function. Both calls await the same promise that is returned by Promise.resolve().

So the await is not necessary? Why?

Because the output you see on the console is not because of await, but because of the promise chain (step 7 above) that is constructed as a result of two invocations of the cmpConcurrentFunction function.

Further more i was expecting this to work :

const disallowConcurrency = (fn) => {   
let inprogressPromise = Promise.resolve();

return async (...args) => {
await inprogressPromise;
inprogressPromise = fn(...args);
};
};

The above code doesn't works as your original code because now the promise chain isn't constructed which is key to the output your original code produces.

With this implementation of disallowConcurrency, your code is executed as explained below:

  1. Call cmpConcurrentFunction function, passing in 'I am called 1 second later' as an argument

  2. Awaiting inprogressPromise pauses the function execution

  3. Call cmpConcurrentFunction for the second time, passing in 'I am called 2 seconds later' as an argument

  4. Awaiting inprogressPromise pauses the function execution. At this point, both invocations of cmpConcurrentFunction are paused and both function invocations are awaiting the same promise that was created as a result of calling Promise.resolve().

    This is the key point here: both invocations of cmpConcurrentFunction function are awaiting the same promise that was created by Promise.resolve(). Why are both invocations awaiting the same promise? Because the cmpConcurrentFunction is invoked for the second time before the first invocation of cmpConcurrentFunction is resumed and calls the initComp function

  5. Resume the first invocation of cmpConcurrentFunction, call the initComp function and assign its return value to inprogressPromise.

    Calling initComp function sets up a timer that resolves the promise initComp function returns after 1 second

  6. Resume the second invocation of cmpConcurrentFunction, call the initComp function and assign its return value to inprogressPromise, overwriting the current value of inprogressPromise which is the promise returned by the first call to initComp function.

    Calling initComp function sets up a timer that resolves the promise initComp function returns after 1 second

At this point, initComp function has been invoked twice, setting up two separate timers that resolve their respective promise after 1 second each.

To summarize the differences between the original implementation of disallowConcurrency function and this one is that in the original implementation, you have a promise chain which resolves the promises sequentially whereas in the second implementation, you have two separate promises (that don't depend on each other) and they are resolved after 1 second each.

What is the difference between async/await forEach and Promise.all + map

The O.P.'s question was asking for clarification on another StackOverflow question found here. For further reading, and many other great answers on this general topic, please take a look at the link.

For Googlers who only saw the question title

Don't use async/await with forEach. Either use a for-of loop, or use Promise.all() with array.map().

If you have a general understanding on promises and async/await, the TL;DR on the differences between promise.all() + array.map() and .forEach(), is that it's impossible to await a forEach() itself. Yes, you can run tasks in parallel in .forEach() just like you can with .map(), but you can't wait for all of those parallel tasks to finish, then do something once they've all finished. The whole point of using .map() instead of .forEach() is so you can get a list of promises, collect them with Promise.all(), then await the whole thing. To see what I mean, just put a console.log('Finished') after a forEach(async () => ...), and you'll see the "finished" get logged out before everything has finished running in the .forEach() loop. My advice would be to just not use .forEach() with async logic (and really there's not a reason use ever use .forEach() anymore these days, as I explain further down).

For those who need something a bit more in depth, the rest of this answer will dive into more detail, first giving a small review on promises, then showing an in-depth explanation on how these approaches behave differently and why .forEach() is always the inferior solution when it comes to async/await.

A primer on promises and async/await

For the purposes of this discussion, you just have to remember that a promise is a special object that's promising that some task is going to be completed at some point in the future. You can attach listeners to a promise via .then(), to get notified when the task is completed, and to receive the resolved value.

An async function is simply a function that will always return a promise, no matter what. Even if you do async function doThing() { return 2 }, it's not going to return 2, it's going to return a promise that immediately resolves to the value 2. Note that an asynchronous function will always return a promise immediately, even if it takes a long time for the function to run. This is why it's called a "promise", it's promising that the function will eventually finish running, and if you want to be notified for when the function has finished, you can add an event listener to it, via .then() or await.

await is special syntax that lets you pause execution of an async function until a promise resolves. await will only effect the function it's directly inside. Behind the scenes, await is simply adding a special event listener to the promise's .then(), so it can know when the promise resolves and what value it resolves with.

async fn1() {
async fn2() {
await myPromise // This pauses execution of fn2(), not fn1()!
}
...
}

async function fn1() {
function fn2() {
await myPromise // An error, because fn2() is not async.
}
...
}

If you can get a good grasp of these principles, then you should be able to understand the next sections.

for-of

A for-of loop lets you execute your asynchronous tasks in serial, one after another. For example:

const delays = [1000, 1400, 1200];

// A function that will return a
// promise that resolves after the specified
// amount of time.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))

async function main() {
console.log('start')
for (const delay of delays) {
await wait(delay)
console.log('Finished waiting for the delay ' + delay)
}
console.log('finish')
}

main()

difference between different scenarios of async await

Why does only 3rd case work??

All async functions return a promise - always. When you return a value in an async function, that value becomes the resolved value of the promise that the async function returns.

In addition, axios() returns a promise so you also need to use .then() or await on its return value to get the result from the axios() call too.

Scenario 1

In scenario 1, Your getAll() function is async and returns a promise. Then your test() function doesn't use await or .then() on that promise:

const test = () => {
let anecdotes = getAll();
// do something
};

Therefore anecdotes is just a promise and you haven't actually gotten the value out of the promise. You need to use await or .then() on that promise to get the value.


Scenario 2

Scenario 2 has a slightly different problem. In getAll()you do this:

export const getAll = () => {
const retrievedData = axios.get('http://localhost:3001/anecdotes');
return retrievedData.data;
}

axios.get() returns a promise. So, retrievedData is a promise. It's not the result you want. When you return retrievedData.data, you're just returning a non-existing property of the promise so it will always return undefined.


Scenario 3

In scenario 3, you do successfully use await on the result of the async function and on the result of the axios() call and thus it works.

export const getAll = async() => {
const retrievedData = await axios.get('http://localhost:3001/anecdotes'); // should return - Array
return retrievedData.data;
};

const test = async () => {
let anecdotes = await getAll();
// do something
};


Related Topics



Leave a reply



Submit