Best practice for running a background process in Node? - javascript

Say for example I have the following code, a simple UI test.
async function testMyCoolUI() {
await uiFramework.openMyApp(url);
await sleep(2000);
await uiFramework.clickButtonX();
await uiFramework.clickButtonY();
}
Now a new requirement gets added. At any point during the test, a popup could come up on the screen saying "Are you a bot?", and we have to select "No".
How would you structure your test such that this "process" can run constantly in the background of the test, watching for this popup? My initial idea is to just kick off an async function polling for the popup, but don't wait on the promise in testMyCoolUI.
async function testMyCoolUI() {
await uiFramework.openMyApp(url);
await sleep(2000);
startPollingForPopup(); // this is an async function, but not waiting on it
await uiFramework.clickButtonX();
await uiFramework.clickButtonY();
}
However this feels wrong, and the promise will sit unresolved and the process won't clean up nicely. What is the way to do this "correctly" in JS?
Other thought:
Promise.all([testMyCoolUI, pollForPopup]);
But in this case, the test would complete still before the polling ever resolved. And Promise.race doesn't really work here for the same reason.

A good code structuring pattern that will ensure proper cleanup is the promise disposer pattern:
async function testMyCoolUI() {
await uiFramework.openMyApp(url);
await sleep(2000);
await withPollingForPopup(async () => {
await uiFramework.clickButtonX();
await uiFramework.clickButtonY();
});
}
async function withPollingForPopup(run) {
try {
startPollingForPopup(); // not waiting for anything
return await run();
} finally {
stopPollingForPopup(); // optionally `await` it
}
}
This assumes a background process, possibly an event subscription, that can be started and stopped.
Alternatively, if the background process does return a promise which rejects on errors and you want to abort as soon as possible, you might use
async function withPollingForPopup(run) {
const poll = runPollingForPopup();
const [res] = await Promise.all([
run().finally(poll.stop),
poll.promise
]);
return res;
}

Related

Call async function without waiting is blocking? [duplicate]

I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();

What if we do not wait for an asynchronous javascript function?

What if we do not wait for an asynchronous javascript function?
As far as I know some languages like C # should not run an asynchronous function unmanaged!
I wanted to know if this is also true for the JavaScript language?
var asynchronousFunction = async function() {
//...
}
function main() {
var result = true;
//...
asynchronousFunction(); // The result of this function has no effect on our output (result)
//...
return result;
}
It's run just the same. (In fact, you never await a function, you await for the the Promise it returns.)
The asynchronous function is run synchronously until the first await or return within it, at which point a Promise is returned to the caller and the rest of the function is arranged to run later.
It's up to the caller to do something (or nothing) to the Promise. After all, you might wish to store the promise in an array and await for the lot of them (Promise.all) or do something more esoteric about it, so JavaScript itself doesn't care.
Some smart enough IDEs and linters are able to raise a warning about unhandled promises, though, especially if you have enough type information to do so (e.g. by using TypeScript).
It's true for javascript as well.
You don't want to just create a promise and leave it totally hanging, if there are errors then they become unhandled errors and if it exits unexpectedly then you have no way of knowing that.
What I recommend is using the Promise.race at the top level and then it will run all of your async functions in parallel but will exit if any one of them exits unexpectedly.
async function worker() {
while (true) {
// do background work in a loop
}
}
async function server() {
await init()
await listen()
}
function main() {
const p0 = worker()
const p1 = server()
try {
await Promise.race([p0, p1])
console.log('done')
return true
} catch (err) {
console.log('The server had an error unexpectedly', err)
return false
}
}
If you expect the promises to eventually exit gracefully then use Promise.all instead, which will wait until all promises exit successfully before resolving.

Is it safe to omit await to let a method run in parallel? [duplicate]

I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();

Drop Collection Before Closing Connection Using Async and Await Javascript

I am trying to clear a collection in my database prior to loading it with data and then closing the connection. I am using MongoDB with Mongoose for the database. My code is as follows:
function closeConnection() {
return mongoose.connection.close()
}
function dropCollections() {
return People.collection.drop()
}
async function doItAll() {
await dropCollections()
await closeConnection()
}
doItAll()
People is a mongoose model. I was under the impression that the await statement would wait for the initial promise to resolve before moving on, but what happens is the connection to the database closes before the collection can be dropped. What is the proper way to achieve this? Can I do it using async and await?
One thing I noticed is that People.collection.drop() returns undefined, but shouldn't the result still be resolved before the connection closes?
Try this once, if not resolved yet:
async function closeConnection() {
return mongoose.connection.close();
}
async function dropCollections() {
return People.collection.drop();
}
async function doItAll() {
const result = await dropCollections();
if (result) console.log("Collection dropped");
await closeConnection();
}
doItAll();
The await keyword will "wait" for Promise functions. Since collection.drop() is a Promise but dropCollections() is not, you can declare it as a promise-like function, so the purpose is served.

Javascript Await/Async Feature - What if you do not have the await word in the function?

I am learning about Javascript ES2017 async/await feature.
Been reading a lot about it, and came across an understanding that await is like yield, and allows us to wait for the promises to complete.
From https://javascript.info/async-await
async function showAvatar() {
// read our JSON
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
The question I have is, can I add await to every single line of code?
Or what happens if I remove the word await?
And another question is, if async/ await makes the code seems to run synchronously and in order, why don't we don't use it at all (means make everything stay synchronous in the first place?)
Thank you!
async functions are just syntactic sugar around Promises. It does nothing to change the function to be synchronous. In fact any function that is async implicitly returns a Promise object.
All await does is provide a convenient way to wait for a Promise. If you remove the await keyword, then the Promise will not be "unwrapped," which is not what you want if your function is going to operate on the result of that Promise.
To illustrate, this is the desugared version of your async function:
function showAvatar() {
let githubUser;
// read our JSON
return fetch('/article/promise-chaining/user.json').then(response => {
return response.json();
}).then(user => {
// read github user
return fetch(`https://api.github.com/users/${user.name}`);
}).then(githubResponse => {
return githubResponse.json();
}).then(user => {
githubUser = user;
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
return new Promise((resolve, reject) => setTimeout(resolve, 3000));
}).then(() => {
img.remove();
return githubUser;
});
}
So await essentially just adds a .then callback to a Promise. Without await being specified, you'll just have a Promise object, not the result of the Promise.
await tells the runtime to wait for the promise returned from the expression on the right-hand side to be fulfilled.
In the following code control is stopped in the async function until the promise returned by the fetch invocation is fulfilled, and the response value sent to the fulfilled promise is printed to the console.
async function go() {
let response = await fetch('http://www.example.com');
console.log(response); // print the response after some time
}
go();
If the await keyword was omitted then control would immediately continue to the next line of the function and the the promise returned by fetch would be immediately printed to the console.
async function go() {
let response = fetch('http://www.example.com');
console.log(response); // print the promise from fetch immediately
}
go();
await is a contextual keyword that only means something special when used inside a function marked async. By marking a function async you are telling the runtime to:
treat the function as a generator function
yield a value where the user types await (usually a promise)
wrap the function in special handling logic so that progress through the function only occurs when each promise yielded by await is fulfilled
In this way, asynchronous control flows can be written in a style closer to the traditional synchronous style. ie. without nesting, callbacks or visible promises. try..catch can also be used in the normal way.
As another answer mentions, async, and await are syntactic sugar that ask the runtime to use existing objects (generator functions and Promises) behind the scenes to make async code easier to read and write.
can you await every line
I would guess you can. If the expression on the right of the await results in a promise, then the async/await behaviour detailed above occurs.
If the expression on the right of the await does not result in a promise, then I would guess the value is wrapped in a resolved promise for you, and logic continues per the above, as though the value came from an immediately resolved promise. This is a guess.
First, async / await, work with promises. If you are not calling from an async function or the await sentence is not then compatible, it won't work. In most cases if you remove await, you are going to end with a Promise and not the value of the promise.
Secondly, there are some cases that you may want to continue with the execution of the code while you are resolving some asynchronous task.

Categories

Resources