setTimeout with Promises - javascript

I trying to solve a challenge with the following question:
Implements a function that takes a number as a parameter and after x milliseconds (between an interval of 1 to 100 ms. Use setTimeout and as floor and random functions from the Math library), without a console or with twice the received parameter. Then, call this function 5 times. Ex.:
Without performing any treatment, it is easy to notice that the order of the values ​​shown on the console is random and does not accept the order of calling the functions. Therefore, to resolve this issue, treat or subscribe to setTimeout using callback, Promise and async / waitit.
That's the expected behavior:
let result;
result = double(5, 0); // returns 10
result = double(12, result); // returns 34
result = double(2, result); // returns 38
The goal is to treat the asynchronous behavior of setTimeout with promises, async functions or callbacks.
That's was what i got until now and with no success:
function promisify(number, increase){
return new Promise(resolve => setTimeout(() => resolve(number * 2 + increase), 100))
}
async function double(number, increase) {
const value = await promisify(number, increase);
return value;
}
let result;
result = double(5, 0)
result = double(10, result)
result = double(20, result)
console.log(result)
I want to return a promise with a set timeout that was a random calculate miliseconds until resolve the double of the number + the increase value if it exists.
Even waiting for the promise to result in the async function, it continues to return a pending promise
The result variables have to increase on each calc but they're receiving functions instead of the double result

You're actually almost there. All you need is to assign the value of the resolved promise to result, instead of assigning the Promise object directly. This is done by using result = await double(<param1>, <param2>).
However, since JS does not yet support top-level await, you need to wrap your whole result assignment logic in another async function, and then call it as such:
function promisify(number, increase){
return new Promise(resolve => setTimeout(() => resolve(number * 2 + increase), 100))
}
async function double(number, increase) {
const value = await promisify(number, increase);
return value;
}
async function run() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
}
run();
However, looking at your code, the double function seems to only serve as a wrapper. Your code can be easily rewritten so that you perform the calculation immediately, but simply wait a little before resolving the promise:
// Simply forces the async operation to wait for a set duration
function wait(duration){
return new Promise(resolve => setTimeout(resolve, duration));
}
async function double(number, increase) {
await wait(100);
return (number * 2) + increase;
}
async function run() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
}
run();
In this way, you can then implement the question's desired randomized settimeout, if you want to, i.e.:
// Waits between [1, 1000] milliseconds
await wait(Math.random() * 1000);

You are close to the solution. The reason why this does not work is the following:
double is an async function. That means it does not return 10, 20, or any other number, but a Promise that resolves to this number as soon as possible (in this case, after the timeout).
That means you should wrap your code into another async function and use await to handle the promises:
async function doPrint() {
let result;
result = await double(5, 0)
result = await double(10, result)
result = await double(20, result)
console.log(result)
return result;
}
doPrint().then(function(result) { console.log('Returned result ' + result); });
Notice that the "then" method represents another way to deal with async functions: It is the usual method of any promise. Just remember that any async function returns a promise under the hood (even when not explicitly specified). The await syntax is just syntactic sugar around dealing with the then calls.

Related

Javascript - what is the difference between Promise.all vs just iterating when doing async operation inside? [duplicate]

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

JavaScript is not waiting for Async Await [duplicate]

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

Promise.all vs [await x, await y] - Is it really the same? [duplicate]

This question already has answers here:
Any difference between await Promise.all() and multiple await?
(6 answers)
Closed 1 year ago.
This is a basic question, but i couldn't find the answer to it anywhere.
We have two approaches:
// consider someFunction1() and someFunction2() as functions that returns Promises
Approach #1:
return [await someFunction1(), await someFunction2()]
Approach #2:
return await Promise.all([someFunction1(), someFunction2()])
My Team Leader said that both approaches ended up in the same solution (both functions executting in parallel). But, from my knowledge, the first approach would await someFunction1() to resolve and then would execute someFunction2.
So that's the question, is it really the same, or are there any performance improvements on second approach? Proofs are very welcome!
No, you should not accept that:
return [await someFunction1(), await someFunction2()];
Is the same as:
return await Promise.all([someFunction1(), someFunction2()]);
I should also note that await in the above return await is not needed. Check out this blog post to learn more.
They are different!
The first approach (sequential)
Let's determine the difference by inspecting how each of the two alternatives works.
[await someFunction1(), await someFunction2()];
Here, in an async context, we create an array literal. Note that someFunction1 is called (a function which probably returns a new promise each time it gets called).
So, when you call someFunction1, a new promise is returned, which then "locks" the async context because the preceding await.
In a nutshell, the await someFunction1() "blocks" the array initialization until the returned promise gets settled (by getting resolved or rejected).
The same process is repeated to someFunction2.
Note that, in this first approach, the two promises are awaited in sequence. There is, therefore, no similarity with the approach that uses Promise.all. Let's see why.
The second approach (non-sequential)
Promise.all([someFunction1(), someFunction2()])
When you apply Promise.all, it expects an iterable of promises. It waits for all the promises you give to resolve before returns a new array of resolved values, but don't wait each promise resolve until waiting another one. In essence, it awaits all the promises at the same time, so it is a kind of "non-sequential". As JavaScript is single-threaded, you cannot tell this "parallel", but is very similar in the behavior point of view.
So, when you pass this array:
[someFunction1(), someFunction2()]
You are actually passing an array of promises (which are returned from the functions). Something like:
[Promise<...>, Promise<...>]
Note that the promises are being created outside Promise.all.
So you are, in fact, passing an array of promises to Promise.all. When both of them gets resolved, the Promise.all returns the array of resolved values. I won't explain in all details how Promise.all works, for that, I suggest you checking out the documentation.
You can replicate this "non-sequential" approach by creating the promises before using the await. Like so:
const promise1 = someFunction1();
const promise2 = someFunction2();
return [await promise1, await promise2];
While promise1 is being waited, promise2 is already running (as it was created before the first await), so the behavior is similar to Promise.all's.
My Team Leader said that both approaches ended up in the same solution (both functions executting in parallel).
That is incorrect.
But, from my knowledge, the first approach would await someFunction1() to resolve and then would execute someFunction2.
That is correct.
Here is a demonstration
Approach 1:
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach1() {
return [await someFunction1(), await someFunction2()];
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach1();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
hello
world
result: hello,world
total time: 1205
Which means that someFunction1 runs to completion first and then someFunction2 is executed. It is sequential
Approach 2:
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach2() {
return await Promise.all([someFunction1(), someFunction2()]);
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach2();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
world
hello
result: hello,world
total time: 803
Which means that someFunction2 finishes before someFunction1. The two are parallel.
Easy to see the difference
function createTimer(ms, id) {
console.log(`id: ${id} started ${new Date()}`);
return new Promise((res, rej) => {
setTimeout( () => {
console.log(`id: ${id} finished ${new Date()}`);
res(id);
}, ms );
});
}
(async function() {
var result1 = [await createTimer(5000, '1'), await createTimer(5000, '2')];
var result2 = await Promise.all([createTimer(5000, '3'), createTimer(5000, '4')]);
console.log(result1);
console.log(result2);
})();
The first one starts 1 and when 1 finishes it starts 2.
The second one starts 3 and 4 at almost the very same moment.
If you start with a function which simulates doing some work which outputs at stages of that work but takes some time. eg
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
And then you duplicate that with a second, similar, method. You plug your 2 methods in and you see that the one using Promise.all does it in parallel but the one using 2 await calls does it in series.
Parallel
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2);
}
}, 1000);
});
}
(async function(){
const result = await Promise.all([someFunction1(),someFunction2()]);
console.log("result",result);
})();
Series
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2);
}
}, 1000);
});
}
(async function(){
const result = [await someFunction1(),await someFunction2()];
console.log("result",result);
})();
Both give the exact same result but getting there is very different.
MDN documentation for Promise.all() states that
This method can be useful for aggregating the results of multiple promises. It is typically used when there are multiple related asynchronous tasks that the overall code relies on to work successfully — all of whom we want to fulfill before the code execution continues.
While it isn't explicit, you can await Promise.all to track multiple promises. Only when all promises are resolved will the code execution continue.
The other approach you mention of capturing separate asynchronous tasks in an array is not the same due to how await operates.
An await splits execution flow, allowing the caller of the async function to resume execution. After the await defers the continuation of the async function, execution of subsequent statements ensues. If this await is the last expression executed by its function, execution continues by returning to the function's caller a pending Promise for completion of the await's function and resuming execution of that caller.
So, each await will pause execution before resuming. No need for a demonstration.

"A little help!" with the asynchronous syntax in javascript. I'm an old newbie, and I've finding this more than a little frustrating

I’d just like to run an async function, have it wait till it has a return value, pass that return value to another async function and wash and repeat a few times. I’ve seen some examples but I’m struggling to make them work in test code. Forgive all the baseball context, but I had to have a little fun with this self-inflicted nose bleed.
I can't even make it work without passing return values in the attached code, feeling pretty stupid at the moment...
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
I'd like to see the syntax to have whosOnFirst(1) execute, pass a return value to whatsOnSecond() and pass it's return value to iDunnosOnthird(). Creating the desired output (less the spaces) referenced in the bottom of the snippet.
I really appreciate the help!
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
waitSec("who's on first", 1).then(result => {
return result;
})
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
waitSec("what's on second", i).then(result => {
return result;
})
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
waitSec("iDunno's on third", i).then(result => {
return result;
})
}
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
// CURRENT OUTPUT:
// who's on first called, waiting 1 second(s)...
// what's on second called, waiting 2 second(s)...
// iDunno's on third called, waiting 3 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second sent 3 in a return, and done!
// iDunno's on third sent 4 in a return, and done!
// DESIRED OUTPUT:
// who's on first called, waiting 1 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second called, waiting 2 second(s)...
// what's on second sent 3 in a return, and done!
// iDunno's on third called, waiting 3 second(s)...
// iDunno's on third sent 4 in a return, and done!
Working Solution
Your issue is that the waitSec is in a different context from the rest of the code. You need to lift it to the Promise context.
Then you have "promisified" code all the way down, and you can chain promises. Here it is working (further explanation below it):
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)
Further Explanation
Your original implementation of waitSec always returns undefined to the caller:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
// return undefined happens here
}
The return inside the setTimeout does not return to your calling code, because your code never calls this function. It is invoked by the JS timer code, asynchronously.
Returning values from async code using a callback
The way to communicate a result out of there to a caller is to either take a callback as a parameter to the outer function and call that callback in the async function, like this:
function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
}
In which case you have the classic callback pattern of JS.
Using Promises, and why you might prefer them
Or, you return a Promise, and resolve it to return the value - as I demonstrated in the solution. For more about that, search for "how do I promisify a callback".
The benefit of doing it this way (with a Promise) is that Promises are composable, as demonstrated in the solution.
Promises are also await-able, so you can use async/await with Promise-returning functions.
You do not need to mark the Promise-returning functions as async.
If you mark a function as async, it returns a Promise, like this:
// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)
// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()
Marking synchronous return functions async makes them composable with async code that actually needs a Promise to return a value. You can use it to lift your functions to the same async context and compose them.
// Synchronous function that is Promisified and composable
async function simple() {
return 3
}
To actually get a value out of an asynchronous function that takes a callback and provides its return via that, you must construct and return a Promise from your function, and then call the Promise resolve inside the callback that provides the value you want to communicate out.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
In this case, you have to explicitly wire up the Promise machinery. This is pretty much the only time that you need to create and return a Promise.
What it is doing is wrapping a callback interface for you.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
Notice in the callback example, you had to take a callback argument from the caller as a parameter, and then call that with the return value inside the callback that provides the value.
With the Promise machinery, you do take a callback argument, but it is not provided by the caller, it is provided by the Promise that you construct. And then you return the Promise to the caller.
The getDBRecordP function above is the essence of "how do I convert a function that takes a callback into a function that returns a Promise?".
The caller then passes in the callback to the the Promise.then() method.
So you have a machine that wraps the callback passing convention into a formal API that is composable, which callbacks are not.
The other aspect of the Promise machine is the .catch() method.
Whereas with callbacks, the convention has always been that a callback signature is (err, result), the Promise machine allows you to provide two callbacks - one for err and one for result.
Please utilize await keyword that is beautiful for async function. Your waitSec function should return a promise, also when you are resolving a promise, your value comes in .then((res)=> console.log(res)), use it for chaining like below:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
whosOnFirst(1).then((firstResp) => {
whatsOnSecond(firstResp).then((secResp) => {
iDunnosOnthird(secResp).then((thirdResp) => {
console.log(thirdResp);
})
})
})
You can also use the values in chain like below with async/await:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
let test = async() => {
var res1 = await whosOnFirst(1);
var res2 = await whatsOnSecond(res1);
var res3 = await iDunnosOnthird(res2);
console.log(res3);
}
test();
You need to await for each result like that:
async function main() {
const result1 = await whosOnFirst(1);
// whosOnSecond will not get called except after the whosOnFirst is done.
const result2 = await whosOnSecond(result1);
}
Here's how it's supposed to work:
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
async function test(){
console.log('no delay, yet');
await delay(1000);
console.log('After 1 second');
await delay(1000);
console.log('2 seconds have passed');
await delay(3000);
console.log('Oh look, 5 seconds have passed!');
}
test();
async functions await until a Promise is resolved, then go to the next block of code after the Promise, possibly awaiting again... and again. I admit I thought it was strange that an async function really runs the awaits synchronously, but the async function itself it asynchronous... meaning another function executed immediately after an executed async function is likely to return results before the async function executes.
In regards to your example:
class Counter{
constructor(increment = 1, initially = 0){
this.increment = increment; this.count = initially;
}
inc(){
return this.count += this.increment;
}
dec(){
return this.count -= this.increment;
}
}
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
const sec = new Counter;
function logCaller(callerName){
console.log(`${callerName} called, after ${sec.inc()} second(s)...`);
}
async function what(){
await delay(1000);
logCaller("who's on first");
await delay(1000);
logCaller("what's on second");
await delay(1000);
logCaller("iDunno's on third");
}
what();

map() function with async/await

There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.

Categories

Resources