I'm making a backend server with Node.js (based on Nest.js). Each route handler mainly queries/modifies the database (main effect), and sometimes send events to external server, leave log and etc (side effect).
I want each route handler functions not to fail when only side effect throws error and main effect goes well(behavior 1). And I also want the side effects to be executed in parallel with the main effect so that the time executing each route handler functions can be decreased(behavior 2).
How can I achieve this behavior in javascript? I first tried this code:
async routeHandler() {
sideEffect().catch(console.error)
await mainEffect();
}
However, when the sideEffect function throws an error, the routeHandler function fails. I can try next, but it does not satisfies the behavior 2.
async routeHandler() {
try {
await sideEffect();
} catch (error) {
console.error(error);
}
await mainEffect();
}
maybe what you are looking to use is Promise.allSettled?
Run async tasks in parallel
the other async tasks should still continue even if the other tasks has been rejected
Link for the docs: check the compatibility section as well.
Related
I was wondering if there is any to cancel / stop execution of a javascript function that contains multiple await functions. Due to the nature of promises and their lack of proper cancellations, is there any other implementation or library to help me achieve something like this?
async function run(x,y,z) {
return new Promise(async(resolve,reject) => {
await doSomething(x)
await doSomething(y)
//cancel could be happen around here and stop the last "doSomething"
await doSomething(z)
})
}
setTimeout(() => {
run.cancel()
},500) //cancel function after 500ms
To just stop the advancement from one function call to the next, you can do something like this:
function run(x, y, z) {
let stop = false;
async function run_internal() {
await doSomething(x)
if (stop) throw new Error("cancelled");
await doSomething(y)
if (stop) throw new Error("cancelled");
await doSomething(z)
}
return {
cancel: () => {
stop = true;
},
promise: run_internal();
};
}
const retVal = run(a, b, c);
retVal.promise.then(result => {
console.log(result);
}).catch(err => {
console.log(err);
})
setTimeout(() => {
retVal.cancel()
}, 500); //cancel function after 500ms
Javascript does not have a generic way to "abort" any further execution of a function. You can set a flag via an external function and then check that flag in various points of your function and adjust what you execute based on that flag.
Keep in mind that (except when using workerThreads or webWorkers), Javascript runs your code in a single thread so when it's running, it's running and none of your other code is running. Only when it returns control back to the event loop (either by returning or by hitting an await) does any of your other code get a chance to run and do anything. So, "when it's actually running", your other code won't be running. When it's sitting at an await, your other code can run and can set a flag that can be checked later (as my example above shows).
fetch() in a browser has some experimental support for the AbortController interface. But, please understand that once the request has been sent, it's been sent and the server will receive it. You likely won't be aborting anything the server is doing. If the response still hasn't come back yet or is in the process of coming back, your abort may be able to interrupt that. Since you can't really know what is getting aborted, I figure it's better to just put a check in your own code so that you won't process the response or advance to further processing based on a flag you set.
You could wrap this flag checking into an AbortController interface if you want, but it doesn't change the fundamental problem in any way - it just affects the API you expose for calling an abort.
The way to actually use cancellation is through the AbortController which is available in the browser and on Node 15+
Node reference: https://nodejs.org/api/globals.html#class-abortcontroller
MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
Some APIs are currently using out of the box the abort signal like fetch in the browser or setTimeout timers API in Node (https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).
For custom functions/APIs you need to implement it by yourself but it's highly encouraged to follow the Abort signal methodology so you can chain both custom and oob functions and make use of a single signal that does not need translation
I got a very annoying problem with PM2's (version 5.1.0) pm2.delete(process, errback) function. After two days of debugging, trying to find the root of the problem, it starts to look like an issue which is related to the PM2 library itself.
Here a simplified code snippet of what I am doing. I will explain the issue afterwards.
const debug = require("debug")("...");
const pm2 = require("pm2");
const Database = require("./Database");
...
class Backend {
static cleanup() {
return new Promise((resolve, reject) => {
pm2.connect((error) => {
if (error) {
debug("...");
reject();
return;
}
// MongoDB query and async forEach iteration
Database.getCollection("...").find({
...
})
.forEach((document) => {
pm2.delete("service_...", (error, process) => {
if (!error && process[0].pm2_env.status === "stopped") {
debug("...");
} else {
debug("...");
}
});
})
.catch(...)
.then(...);
}
});
}
...
}
Now to the problem: my processes do not get terminated and errback of pm2.delete(process, errback) is not executed AT ALL.
I printed the error parameter of the connect callback and it is always null, hence a connection to PM2 is established successfully
I placed debug text directly at the beginning of the delete callback and it is not printed
I wrapped the delete function in a while loop which only stops if the delete callback is executed at least once and the loop runs forever
I started a debug session and noticed that PM2's delete function in node_modules/pm2/lib/API.js (line 533) gets called, but for some reason the process does not get terminated and my provided callback function is not executed at all (I went through the commands step by step in debug mode but still can not tell where it fails to execute the callback (it seems to happen in PM2's API.js though))
I also noticed that when running the code step by step in debug mode with breakpoints that sometimes my process gets terminated with the API call if I cancle the execution at a certain point in between (however, the callback was still not executed)
I use PM2's delete function at another place of my software as well and there it is working like a charm
So for some reason the pm2.delete(process, errback) is not executed correctly and I don't know what to do at this point. Is someone experienced with PM2's source code or had a similar issue at some point? Any advice would be helpful.
It looks like I found the root of the problem:
At a later point in the promise chain after the forEach call, I use pm2.disconnect();. After further investigation I noticed that it is not perfectly synchronized which means in my case that PM2 gets disconnected before the delete function is completely finished. This gives the described results and the weird debugging behaviour.
All in all, the API works therefore perfectly fine but one has to pay very close attention to asynchronous code as it can cause really complicated behaviour which is also hard to debug.
One has to make sure that the delete functions are really finished before pm2.disconnect(); is called.
I'm having trouble understanding control flow with asynchronous programming in JS. I come from classic OOP background. eg. C++. Your program starts in the "main" -- top level -- function and it calls other functions to do stuff, but everything always comes back to that main function and it retains overall control. And each sub-function retains control of what they're doing even when they call sub functions. Ultimately the program ends when that main function ends. (That said, that's about as much as I remember of my C++ days so answers with C++ analogies might not be helpful lol).
This makes control flow relatively easy. But I get how that's not designed to handle event driven programming as needed on something like a web server. While Javascript (let's talk node for now, not browser) handles event-driven web servers with callbacks and promises, with relative ease... apparently.
I think I've finally got my head around the idea that with event-driven programming the entry point of the app might do little more than set up a bunch of listeners and then get out of the way (effectively end itself). The listeners pick up all the action and respond.
But sometimes stuff still has to be synchronous, and this is where I keep getting unstuck.
With callbacks, promises, or async/await, we can effectively build synchronous chains of events. eg with Promises:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
});
Great. I've got a series of tasks I can do in order -- kinda like more traditional synchronous programming.
My question is: sometimes you need to deviate from the chain. Ask some questions and act differently depending on the answers. Perhaps conditionally there's some other function you need to call to get something else you need along the way. You can't continue without it. But what if it's an async function and all it's going to give me back is a promise? How do I get the actual result without the control flow running off and eloping with that function and never coming back?
Example:
I want to call an API in a database, get a record, do something with the data in that record, then write something back to the database. I can't do any of those steps without completing the previous step first. Let's assume there aren't any sync functions that can handle this API. No problem. A Promise chain (like the above) seems like a good solution.
But... Let's say when I call the database the first time, the authorization token I picked up earlier for it has expired and I have to get a new one. I don't know that until I make that first call. I don't want to get (or even test for the need for) a new auth token every time. I just want to be able to respond when a call fails because I need one.
Ok... In synchronous pseudo-code that might look something like this:
let Token = X
Step 1: Call the database(Token). Wait for the response.
Step 2: If response says need new token, then:
Token = syncFunctionThatGetsAndReturnsNewToken().
// here the program waits till that function is done and I've got my token.
Repeat Step 1
End if
Step 3: Do the rest of what I need to do.
But now we need to do it in Javascript/node with only async functions, so we can use a promise (or callback) chain?
let Token = X
CallDatabase(Token)
.then(check if response says we need new token, and if so, get one)
.then(...
Wait a sec. That "if so, get one" is the part that's screwing me. All this asynchronicity in JS/node isn't going to wait around for that. That function is just going to "promise" me a new token sometime in the future. It's an IOU. Great. Can't call the database with an IOU. Well ok, I'd be happy to wait, but node and JS won't let me, because that's blocking.
That's it in a (well, ok, rather large) nutshell. What am I missing? How do I do something like the above with callbacks or Promises?
I'm sure there's a stupid "duh" moment in my near future here, thanks to one or more of you wonderful people. I look forward to it. 😉 Thanks in advance!
What you do with the .then call is to attach a function which will run when the Promise resolves in a future task. The processing of that function is itself synchronous, and can use all the control flows you'd want:
getResponse()
.then(response => {
if(response.needsToken)
return getNewToken().then(getResponse);
})
.then(() => /* either runs if token is not expired or token was renewed */)
If the token is expired, instead of directly scheduling the Promise returned by .then, a new asynchronous action gets started to retrieve a new token. If that asynchronous action is done, in a new task it'll resolve the Promise it returns, and as that Promise was returned from the .then callback, this will also then resolve the outer Promise and the Promise chain continues.
Note that these Promise chains can get complicated very quick, and with async functions this can be written more elegantly (though under the hood it is about the same):
do {
response = await getResponse();
if(response.needsToken)
await renewToken();
} while(response.needsToken)
Fist of all, I would recommend against using then and catch method to listen to Promise result. They tend to create a too nested code which is hard to read and maintain.
I worked a prototype for your case which makes use of async/await. It also features a mechanism to keep track of attempts we are making to authenticate to database. If we reach max attempts, it would be viable to send an emergency alert to administrator etc for notification purposes. This avoid the endless loop of trying to authenticate and instead helps you to take proper actions.
'use strict'
var token;
async function getBooks() {
// In case you are not using an ORM(Sequelize, TypeORM), I would suggest to use
// at least a query builder like Knex
const query = generateQuery(options);
const books = executeQuery(query)
}
async function executeQuery(query) {
let attempts = 0;
let authError = true;
if (!token) {
await getDbAuthToken();
}
while (attemps < maxAttemps) {
try {
attempts++;
// call database
// return result
}
catch(err) {
// token expired
if (err.code == 401) {
await getDbAuthToken();
}
else {
authError = false;
}
}
}
throw new Error('Crital error! After several attempts, authentication to db failed. Take immediate steps to fix this')
}
// This can be sync or async depending on the flow
// how the auth token is retrieved
async function getDbAuthToken() {
}
I'm trying to find a general way to handle errors on promise chains. In the following snipped I'd like to handle an potential connection-error directly in my connection.js.
connection.js
function getData() {
return fetch(myEndpoint)
.catch(err => handleConnectionError(err)) // redirect to error page or similar
}
app.js
// import connection
connection.getData()
.then(data => handleData(data.foo))
So there are two ways this scenario could play out:
If I catch the error in connection.js without throwing a new one, it will continue the promise chain and handleData() will fail
If I throw an Error again after handling it, the Promise chain wont be executed any further, but then I have an unhandled-promise-rejection error in the console.
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
The best way to go about this would be to have a final catch to take care of all errors. E.G:
function errorHandler(e) {
if (e instanceof x) {
//handle here
}
}
And then:
fetch(endPoint).then(doSomething).then(doAnotherThing).catch(err => errorHandler(err))
If fetch or any other promise along the chain produces an error, the rest of the then() statements will be skipped and will be catched by the final catch function. From there you will need to make sure that all types of errors that could be thrown by all promises are taken care of.
However, you will need to get rid of the catch in getData as it will cause the rest of the chain to run as usual since the error is taken care of.
Hope that helps
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
Yes, that's precisely what you should do:
function getData() {
return fetch(myEndpoint)
}
// elsewhere:
connection.getData().then(handleData, handleConnectionError);
You usually don't know whether you always want to "redirect to error page or similar". Maybe at some places in your app, you can handle connection errors by faking response data, and in other places you might want to show an error message instead of triggering a redirect.
I am writing an express application using
NodeJS v8
express (latest version)
After looking at the onHeaders module and finding out how the module rewrites the HTTP head, I wanted to make use of that function of JavaScript.
I wanted to write a small session system using my SQL server. I am aware of the session module from express, but this module is not able to handle the specific tables and customization, I need.
For convenience reasons I wanted the session to be inserted into the request before the controllers and saved after all controllers finished. (e.g. the writeHead method has been called)
My code in the session looks like:
core = async function(req, res, next) {
res.writeHead = hijackHead(res.writeHead); // Hijack the writeHead method to apply our header at the last
}
//[...](Omitted code)
hijackHead = function(writeFunction) {
let fired = false;
return function hackedHead(statusCode) {
if ( fired ) {
return;
}
//[...](Omitted code)
debug("Session data has changed. Writing");
sessionManager.storeSessionData(session.identifier, session).then(() => { // Promise taking ~60ms to resolve
debug("Finished writing...");
callMe(); // Current idea of calling the writeHead of the original res
});
let that = this, // Arguments to apply to the original writeHead
args = arguments
function callMe() {
debug("Finished! Give control to http, statuscode: %i", statusCode);
writeFunction.apply(that, args); // Call the original writeHead from the response
debug("Sent!")
}
} // End of hackedHead
} // End of hijackHead
The core function is being passed to express as a middleware.
Additionally sessionManager.storeSessionData is a Promise storing data and fulfilling after that, taking ~60ms. The Promise has been testes and works perfectly.
When I now make a request using this Controller, the Node net Module returns the error:
TypeError: Invalid data, chunk must be a string or buffer, not object
at Socket.write (net.js:704:11)
at ServerResponse._flushOutput (_http_outgoing.js:842:18)
at ServerResponse._writeRaw (_http_outgoing.js:258:12)
at ServerResponse._send (_http_outgoing.js:237:15)
at write_ (_http_outgoing.js:667:15)
at ServerResponse.end (_http_outgoing.js:751:5)
at Array.write (/usr/lib/node_modules/express/node_modules/finalhandler/index.js:297:9)
at listener (/usr/lib/node_modules/express/node_modules/on-finished/index.js:169:15)
at onFinish (/usr/lib/node_modules/express/node_modules/on-finished/index.js:100:5)
at callback (/usr/lib/node_modules/express/node_modules/ee-first/index.js:55:10)
Since the new function needs about 30ms to react and return the Promise, the function finishes earlier causing Node to crash.
I already tried blocking the Node loop with a while, timeout or even a recursive function. Neither of them worked.
I tries to simplfy the code as much as possible and I hope that I didn't simplify it too much.
Now I am asking if anybody can help me, how to call the writeHead function properly after the Promise has resolved?
The issue with this is, that net.js directly responds to those methods when writeHead has finished. Even though the head has not been written, it tries to write the body.
Instead it is possible to catch the end()method which will await everything and then close the connection.