Await several async functions, but continue when one finishes - javascript

I'm trying to write a function that searches using 2 algorithms. The main function should call both main algorithm functions simultaneously, but continue as soon as one finishes. It should also stop the other function from running.
I currently have an entry function set up like this:
async function entry(code) {
let [ product_algorithm_1, product_algorithm_2 ] = await Promise.all([
get_info_algorithm_1(code),
get_info_algorithm_2(code)
])
// Here I would check which variable is not empty, and display the results
}
This works fine, but the issue is that it waits for both functions to finish before continuing. I'm trying to continue when one finishes, and deleting the others process. Does anyone know how to achieve this?

What you're looking for is Promise.race(). It will return when one of the calls has resolved. Find out more on MDN.
async function entry(code) {
let product_algorithm_fastest = await Promise.race([
get_info_algorithm_1(code),
get_info_algorithm_2(code)
])
}
Its worth noting that the returned value will be that of the promise that was "fastest"

Related

Stopping an asychronous loop

I have an asynchronous function that runs in a loop. It should run indefinitely unless one of the inner async functions rejects or the application is terminated. It should not impact the rest of the application.
import { setTimeout } from 'timers/promises';
let executing = true;
const example = async () => {
while (executing) {
await setTimeout(1000);
await someOtherFunc();
await yetAnotherFunc();
}
}
In my code I execute it without awaiting (since that blocks further execution). Later, I stop the loop by changing the value.
const main = async () => {
// don't await
example();
// ... do stuff 'in parallel' to the asynchronous loop
// possibly weeks later, elsewhere in the code
executing = false;
}
main().then();
(Emphasis that the examples are contrived for Stack Overflow.)
It works, but it feels like a bit of a hack. Testing the execution/stop logic is tricky because the loop logic executes without the remaining test logic waiting. I need to mock the contents of the loop (the various async functions) for testing but because the test continues to execute without awaiting, the test later resets the mocks and the inner loop logic then tries to make unmocked calls.
What is the idiomatic way of achieving this in javascript / nodejs? I feel like a callback would allow me to keep using promises but I can't quite figure out how to achieve it.
// don't await
There's the problem. Your main function definitely should wait until the loop has completed before returning (fulfilling its promise) to its caller.
You can achieve that by
async function main() {
const promise = example();
// ... do stuff
executing = false;
await promise;
}
or better (with proper error handling)
async function stuff() {
// ... do stuff
executing = false;
}
async function main() {
await Promise.all([example(), stuff()];
}
Btw you probably shouldn't have a global executing variable. Better have one per call to example(), so that you can stop each running loop individually. Pass a mutable token object as an argument for that.

Async await - does await block other code from running?

In javascript, does await block code? For example, let's say we have the below code:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Also, let's say that we add some code between the awaits that is not a promise, something like this:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(() => console.log('Do something that takes 3 seconds'), 3000);
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
I would also like to add that I have been studying and reading on both stackoverflow and other resources, but I need some clarification.
Will "selectUser" wait to run until addUser is finished so that we can
select the user that is added?
From MDN Docs - await:
The await expression causes async function execution to pause until a
Promise is settled (that is, fulfilled or rejected), and to resume
execution of the async function after fulfillment. When resumed, the
value of the await expression is that of the fulfilled Promise.
Execution of queryDB function will be paused while it waits for the first promise to settle. Second line of code will only execute after first one has completed successfully.
Will "selectUser" wait for addUser but not the setTimeout?
Yes, that is correct.
If so, how would you write the above code to make addUser run first, then
setTimeout and then selectUser?
You could wrap setTimeout in a function that returns a promise and then await that promise to make sure last line executes after first two have completed.
Following is an example of a wrapper function that wraps setTimeout
function waitForTimeout(seconds) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Hello World");
resolve();
}, seconds * 1000);
});
}
Once you have a wrapper function, you can await it as shown below:
async function queryDB() {
const addUser = await promisePool.execute(
"INSERT INTO Users (User) VALUES ('username')"
);
await waitForTimeout(3); // wait for 3 seconds
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Important note: blocks is probably a bad word here. A better word is it waits. Even better would be probably to just say that operations that are done using await syntax are guaranteed to be executed sequentially.
The Big Picture
The main reason for using asynchronous code in Node is that we don't block the whole app while we wait for some asynchronous operation like a database request, a network request, a file operation etc. - we only block this particular execution context.
Even though Node.js only uses one thread for executing user code, but I/O operations are asynchronous and they're non-blocking.
So imagine you have an endpoint with the code that you presented above, that is linked to some button "Add user" on the front end.
Bill presses the button, you start handling the request, start waiting for addUser operation
At this point John also presses same button. Your code will still be executed and will also start to wait until addUser finishes.
Let's say that Users table has gotten into a deadlock and any db operations with it will be super slow, and we have a third user, Jane, who is just browsing your site but she doesn't need to sign in/sign up, so she doesn't touch Users table. What happens? Jane is totally fine, her requests will go through without any issues.
How to await setTimeout
setTimeout is coming from "previous era" so it uses callback instead of async/await syntax. But it is always possible to convert callback style to async/await, if you need it.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await delay(5000);
In short, await only blocks the code(s) that's after the await statement in the current async function, not the whole thread. Once the await has been resolved the rest of the code gets executed. In your example, since you have 2 await statements, the second one won't run until the first one has finished, because the first one blocks the function that both await statements are inside of. For the second example, since setTimeout() doesn't return a promise and you aren't awaiting it either, the function will treat it as a 'synchronous' operation. That is, it will throw the setTimeout() callback into the call stack till the next event loop and continue executing the next line which is another await statement that will block the function again until it is resolved.
In order to execute all three lines one by one, you'll have to put the setTimeout() into a promise and await it so that JS treats it as an asynchronous operation and 'waits' for it to finish before moving on to the next line.
Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Yes.
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
"selectUser" will be executed after "addUser", but not the setTimeout callback. If you want to run "selectUser" after setTimeout, you can add it inside the setTimeout callback function. So it should look like:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(async () => {
const selectUser = await promisePool.execute("SELECT User FROM Users");
}, 3000);
}
Short answer is yes, it blocks. The purpose of await is, to make sure you have the result available in the next line. So if you needed the value of addUser to make the next query, you would have it.
It was introduced so you don't have to write then() at the end of the first line and put the second call into a callback, which makes the code hard to read.

Promise async call

I am new to node js and trying to understand how to make async calls.
I am trying to wrap a function into a promise in order to make it async. For simplicity, the function is just a loop that takes time :
var p = new Promise(resolve => {
for (let i=0;i<999999999;i++){}
resolve('foo')
});
p.then(function (value) { console.log(value);});
console.log('bar');
I am expecting to see :
bar // printed immediately
foo // printed after the loop finishes
Instead, they are both printed after the loop completion.
How to make the loop block run asynchronously?
Ps. Apologies for the indentation/formatting, I had to write this from a phone.
Thanks
You seem to assume that "asynchronous code" involves concurrency, in the sense that code will run at the same time in another thread. That is not the case. Unless you start another thread by purpose, JS itself will run in a single thread. Therefore no matter what you do with promises: Either the loop runs first and then the logs run or the logs run first and then the loop runs.
You could also achieve concurrent execution through multitasking: If you stop the loop inbetween, other code can run in the meantime:
(async function() {
while(true) {
console.log("loop");
await Promise.resolve();
}
})();
console.log("in the meantime");
But there's nothing asynchronous about your promise. Creating a promise starts the execution of the function, and JS always runs to completion.
Typically, a promise is used to initiate something asynchronous, like an API call, or even a plain setTimeout, that runs outside JS's thread. Your code, however, will iterate through the empty loop bajillion times, and only after that any following lines will be run.
Replace the empty line with a timeout, and it will become async:
var p = new Promise(resolve => {
setTimeout(() => resolve("foo"), 2000);
});
p.then(function(value) {
console.log(value);
});
console.log('bar');

Tell forEach to wait, and mostly avoid wrapping everything inside an async function to use await?

So I got a problem, here is the code:
const resultsArray = [];
function parseDoc(doc) {
const docStringified = JSON.stringify(doc);
console.log(docStringified);
docObject = JSON.parse(docStringified);
resultsArray.push(docObject.name);
};
resultsCollections.forEach(parseDoc);
console.log(resultsArray);
I'm using MongoDB, so resultsCollections is a CommandCursor that I put in a forEach to push the results inside the Array so I can use Array.every() to match if a collection already exists and if not it creates one.
Now if you look at the code right now, this is what would happen in the logs:
> []
> whatever would come from docstringify
> and if there is a second collection, it would be printed here too, because of forEach
Now my problem here is that, if you look at the order of the program, resultsArray is after resultsCollections.forEach(parseDoc) but it prints first.
What I want the log to be is this:
> whatever would come from docstringify
> and if there is a second collection, it would be printed here too, because of forEach
> []
And that without doing a lot, if possible.
What I know is, thats because forEach... has something async in it and basically it's like this, "If you don't put await inside me, I won't wait".
So I wrapped it inside async and tested it without the console.log inside the async function, which resulted [] still being printed first, because async function is the same problem.
So I am forced to put console.log(resultsArray); inside the async function and call the async function.
Now my problem is, since I have other functions that have to be executed, would be executed to quickly before the async function ends, I have to wrap nearly everything inside an async function and call the other functions to avoid lines being executed after javascript reads like, async and then it continues.
How can I prevent putting and wrapping my entire code inside an async function and use await for everything, and then call the other functions, how can I avoid that?
I've also tried it with a Promise by just using .then after the toArray() because that one can return a promise, but everything that is outside of the promise still gets executed while the promise isn't even finished.
I want to tell forEach, that after it finished execute console.log
What are the possibilities to do this? I try to search all, everything that is possible. Because the current, putting async and call other functions is kinda huh... a lot like when it comes to how the code looks later. But if there is no other possibility, well then I guess I can't decide.
I appreciate any help
Cursor.forEachs second parameter is a function called back after the first callback was executed. Use that to work with the resulting array.
resultsCollections.forEach(parseDoc, function() {
console.log(resultsArray);
//...
});
Alternatively you can await the returned promise:
(async function() {
const resultsArray = [];
function parseDoc(doc) { /*...*/ }
await resultCollections.forEach(parseDoc);
console.log(resultsArray);
})();
How can I prevent putting and wrapping my entire code inside an async function and use await for everything, and then call the other functions, how can I avoid that?
You just have to make the functions async that do actually do asynchronous tasks.

Waiting for loops to finish by using await on the loop

Synchronicity in js loops is still driving me up the wall.
What I want to do is fairly simple
async doAllTheThings(data, array) {
await array.forEach(entry => {
let val = //some algorithm using entry keys
let subVal = someFunc(/*more entry keys*/)
data[entry.Namekey] = `${val}/${subVal}`;
});
return data; //after data is modified
}
But I can't tell if that's actually safe or not. I simply don't like the simple loop pattern
for (i=0; i<arrayLength; i++) {
//do things
if (i === arrayLength-1) {
return
}
}
I wanted a better way to do it, but I can't tell if what I'm trying is working safely or not, or I simply haven't hit a data pattern that will trigger the race condition.
Or perhaps I'm overthinking it. The algorithm in the array consists solely of some MATH and assignment statements...and a small function call that itself also consists solely of more MATH and assignment statements. Those are supposedly fully synchronous across the board. But loops are weird sometimes.
The Question
Can you use await in that manner, outside the loop itself, to trigger the code to wait for the loop to complete? Or is the only safe way to accomplish this the older manner of simply checking where you are in the loop, and not returning until you hit the end, manually.
One of the best ways to handle async and loops is to put then on a promise and wait for Promise.all remember that await returns a Promise so you can do:
async function doAllTheThings(array) {
const promises = []
array.forEach((entry, index) => {
promises.push(new Promise((resolve) => {
setTimeout(() => resolve(entry + 1), 200 )
}))
});
return Promise.all(promises)
}
async function main () {
const arrayPlus1 = await doAllTheThings([1,2,3,4,5])
console.log(arrayPlus1.join(', '))
}
main().then(() => {
console.log('Done the async')
}).catch((err) => console.log(err))
Another option is to use generators but they are a little bit more complex so if you can just save your promises and wait for then that is an easier approach.
About the question at the end:
Can you use await in that manner, outside the loop itself, to trigger the code to wait for the loop to complete? Or is the only safe way to accomplish this the older manner of simply checking where you are in the loop, and not returning until you hit the end, manually.
All javascript loops are synchronous so the next line will wait for the loop to execute.
If you need to do some async code in loop a good approach is the promise approach above.
Another approach for async loops specially if you have to "pause" or get info from outside the loop is the iterator/generator approach.

Categories

Resources