Send response and continue to perform tasks Express | Node.js - javascript

In Node.js (which I'm new to) I am trying to perform a series of tasks after receiving a response. However, I want to make the response time as fast as possible. I don't need to return the results of these tasks to the client, so I'm trying to return the response immediately.
My current implementation is roughly:
var requestTime = Date.now;
app.post('/messages', function (req, res) {
console.log("received request");
// handle the response
var body = res.body;
res.send('Success');
res.end();
console.log("sent response");
performComplexTasks(body)
})
function performComplexTasks(body){
// perform data with body data here;
console.log("finished tasks:", Date.now()-requestTime, "ms");
}
// -------LOG-----------
// received request
// POST /api/messages 200 3.685 ms - 59
// sent response
// finished tasks: 2500ms
The client making the request seems to hang until performComplexTasks() is finished. (The POST finishes in 3.685ms, but the response takes 2500ms to finish.)
Is there a way to send the response immediately and complete other tasks without having the client wait/hang? (In my case, the client cannot make multiple API calls.)

If your job is not super-CPU-intense and you can tolerate some work on the main server thread, then just use await to break the execution so that the request can be properly sent. You can use setTimeout or await.
// This line is wrong too - ask a separate question if needed
var requestTime = Date.now;
app.post('/messages', async function (req, res) {
console.log("received request");
// handle the response
var body = res.body;
res.status(200).send({ success: true });
console.log("sent response");
// Method 1:
await performComplexTasks(body)
// Method 2:
setTimeout(() => performComplexTasks(body), 0);
})
async function performComplexTasks(body){
// The line below is required if using the `await` method -
// it breaks execution and allows your previous function to
// continue, otherwise we will only resume the other function after
// this function is completed.
await new Promise(resolve => setTimeout(resolve, 1));
// perform data with body data here;
console.log("finished tasks:", Date.now()-requestTime, "ms");
}
This isn't really a fantastic solution and you'd need to use worker threads for long operations.

Am I right that you're trying to execute a CPU-intensive job in performComplexTasks? If so, then event loop is being locked by that task and new requests are waiting until the job is finished.
It's a bad practice in node.js to execute such 'complex' tasks in the same process as http server. Consider using background workers, queues or something like that.
See this topic for details: Node.js and CPU intensive requests

Related

API calls not returning response with MySQL data streams

I have the following code:
await axiosAPICall(dummyData); // works
const sqlQuery = `SELECT column1, column2 FROM table`;
const queryStream = mySqlConnectionInstance.query(sqlQuery, []);
queryStream
.on('error', function (err) {
// Handle error, an 'end' event will be emitted after this as well
})
.on('result', async (actualData) => {
// axios api call, the api callback goes to callstack with any of the following errors:
// 1. read ECONNRESET
// 2. Client network socket disconnected before secure TLS connection was established
await axiosAPICall(actualData); // breaks
})
.on('end', function () {
// all rows have been received
});
As you can see I'm getting all the rows from a table in a MySQL database stream. When the data comes from the database stream, I'm passing that data to the axios API call.
The API call works perfectly fine when called outside of the stream logic but when I call the API inside the streaming logic, it breaks all the time.
I am hitting API calls as fast as each on('result') gets called (the async/await does NOT slow down the request rate, i.e. I end up with multiple requests in parallel.
Does anyone know why is API calls not working inside the streaming logic section?
If the question needs any clarifications please comment.
Based on a comment that suggests the error is due to making "too many requests" at once - this is a simple and naive way to wait for the previous request before making the next
const sqlQuery = `SELECT column1, column2 FROM table`;
const queryStream = mySqlConnectionInstance.query(sqlQuery, []);
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
let previous = Promise.resolve();
queryStream
.on('error', function (err) {
// Handle error, an 'end' event will be emitted after this as well
})
.on('result', async (actualData) => {
// wait until previous request has completed
await previous;
// optional, add a delay,
// 100ms for this example -
// for example if there is a limit of 10 requests per second
// adjust (or remove) as required
await wait(100);
// set "previous" for next request
previous = axiosAPICall(actualData);
})
.on('end', function () {
// if you `await previous` here,
// you can truly wait until all rows are processed
// all rows have been received
});

Websocket event listener locked when executing another function

I programmed an API in the lastest version of NodeJS.
Its goal was to have a RESTful interface where the user could send code
and a Websocket interface that the code can use
The only problem is that i can't manage to run the event listener client.on('data' at the same time as my RESTful method.
So if i do
global.writeRequest("#get_pos 1") // will request data from my Websocket Server and respond within 10ms~
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 4000); // wait 4 second to make sure the latency of the request doesn't affect the result
console.log("this is a test");
The request gets displayed last even though it should be displayed first.
Result of this command:
this is a test // our console.log
POST /api/data 200 2.373ms -12 // end of the RESTful Method
Server says: %pos[1]:488 // result of our global.writeRequest from earlier except it's shown at the end
Even weirder is that the event listener seems to be locked down during the whole RESTful method which is a problem considering i want to send and use the data coming from my event listener.
Even weirder is that when i do multiple global.writeRequest in one method like this:
global.writeRequest("#get_pos 1")
global.writeRequest("#get_pos 2")
global.writeRequest("#get_pos 3") // will request data from my Websocket Server and respond within 10ms~
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 4000); // wait 4 second to make sure the latency of the request doesn't affect the result
console.log("this is a test");
i end up with the following
this is a test // our console.log
POST /api/data 200 2.373ms -12 // end of the RESTful Method
Server says: %pos[1]:488%pos[2]:488%pos[3]:488
But it should display the following instead befoore the console.log:
Server says: %pos[1]:488
Server says: %pos[2]:488
Server says: %pos[3]:488
So i theorised that the event listener was locked when i executed another function so i tried to put all the functions in async but i got the same issue.
The code:
client_websocket.js
const net = require('net');
const client = new net.Socket();
const port = 50000;
const host = '127.0.0.1';
const password = "something";
client.connect(port, host, async function () {
console.log('Connected');
client.write(password);
});
client.on('close', async function () {
console.log('Connection closed');
});
writeRequest = async function (commmand) {
client.write(commmand);
return "Command sent!"
};
client.on('data', async function (data) {
console.log("Server says: " + data);
}
requests_api.js
var Task = {
sendData: async function (id) {
console.log("code: " + id.code);
return evalCode(id.code);
}
};
async function evalCode(code) {
global.getAllStats();
return eval(code);
}
my eval(code); can then use the websocket interface
Any idea to unlock my event listener while doing my RESTful method?
It appears that Atomics.wait() is blocking the main JS thread so NO other events on that thread (including any socket events) can be processed until it's done.
You need to understand Javascript's event model and really shouldn't ever be using Atomics.wait() on the main server thread as it blocks processing of all other events.
It isn't entirely clear what problem you're trying to solve with Atomics.wait(), but if you just want to run some code after a period of time, then use a setTimeout() and put the code into that callback. That will allow events to be processed while waiting for the timer to fire. This is an important aspect of Javascript's event model.

Angular 2 Rxjs creating action after server response

I am a new in Angular2(and especially to RxJs library) and stacked on next step. My web site starts with a register page.
And after the completion of of register form i have base service.auth.ts method register usage
registerUser(){
this.authService.registerUser(this.registrationForm.value)
.subscribe((data) => {
console.log(data);
})
//this line should apear after server response from backend
console.log('AFTER SERVER RESPONSE')
}
And i recieve response like this
I read some stuff about flatMap method in RxJs documentation and it is claimed to be alternative to default Promise method then() - but i failed to create working instance of code that acutally implements flatMap method for my case
So the question is : How can i achieve result so 'AFTER SERVER RESPONSE' message appears AFTER server response? (not before as mentioned on previous screenshot)
You should wait for asynchronous task to complete. Move that console.log('AFTER SERVER RESPONSE') inside your subscribe block.
registerUser() {
this.authService.registerUser(this.registrationForm.value)
.subscribe((data) => {
console.log(data);
//this line should apear after server response from backend
console.log('AFTER SERVER RESPONSE')
});
// anything written over here will execute before `registerUser()` returns data
console.log('THIS WILL PRINT BEFORE SERVER RESPONSE')
}

Node server not handling async post requests from client properly

We have a node server which doesn't handle post requests properly when they are made asynchronously. When the requests are made synchronously, it handles them fine.
There is a node api server and to mimic a client, there is node script which makes a post request to the server.
While making a single post request or post requests in a loop synchronously, everything works as expected.
While making asynchronous post requests in a loop to the server, the code doesn't execute properly.
Here is the code on the server side. This method is called from router.post() method.
async insert(params) {
let account = new Account();
try {
let totalLicenses = await this.getLicenses(params);
if (totalLicenses === 0) throw new AccountError('NO_AVAILABLE_LICENSE');
let accountResponse = await account.insert(params);
let useLicense = await license.use(accountResponse, params);
/*
Do other account setup stuff here
*/
return accountResponse;
} catch(err) {
throw err;
}
}
getLicenses(params) is a async function that prepares the sql query and awaits the response from a queryDb method which wraps the callback in a promise. Here is the code:
getLicense(params) {
let vals = [...arguments],
sql = 'my sql query';
try {
return await queryDb(sql, val);
} catch (err) {
throw new Error()
}
}
We are using mysql package to talk to db. This is what queryDb method looks like.
queryDb(query, vals) {
return new Promise( (resolve, reject) => {
connection.query(query, vals, (err, results, fields) => {
if (err) reject(err);
resolve(results);
}
}
}
While making asynchronous post requests in a loop to the server, the insert method above executes this.getLicenses(params) method for the requests and then calls the account.insert(params) for the requests and then license.use(accountResponse, params) for the requests.
This becomes a problem when let's say a client has 3 licenses available and they send 5 asynchronous post requests. Technically, it should throw error for the last 2 requests but that is not the case. What ends up happening is, it inserts all 5 accounts since it calls this.getLicenses(params) for all 5 requests before proceeding to insert the account.
Any help is appreciated!

How to use setInterval (with clearInterval) to send requests using Bluebird Promises?

I'm using node-request for sending the requests to server to get some report. The thing is server needs some time to generate the report, so it responses with the report state. I'm checking the report state with setInterval() function, and use clearInterval() when server sends ready response. But with this approach, even after I use clearInterval, responses of earlier requests keep coming, and the response handler runs again and again. This does not cause a lot of harm, but still I believe it can be done better.
Here is my code:
checkForReportReady = setInterval =>
#request URL, options, (err, res, body) =>
console.log err if err
body = JSON.parse body
if body['status'] is 'ready'
clearInterval checkForReportReady
#processReport body
, 1000
What I need: make a request, wait for response, check the status, if status is not ready - make another request after some timeout, repeat until the status code in response is ready. If the status is ready - exit the loop (or clear the interval) and run #processReport.
I tried to make promisified request, and put it into setInterval, but the result was the same.
P.S. I do not control the server, so I can't change the way it responds or deals with the report.
I would recommend not to put requests in an interval callback. This can get ugly when they a) fail b) take longer than the interval.
Instead put a setTimeout in the success handler and try again after (and only if) receiving a response.
This is rather easy with promises:
request = Promise.promisifyAll require 'request'
getReport = () =>
request URL, options
.spread (res, body) =>
body = JSON.parse body
if body.status is 'ready'
body
else
Promise.delay 1000
.then getReport # try again
getReport().then(#processReport, (err) -> console.log(err))
It seems like you can just use a setTimeout() in your response handler:
function checkForReportReady() {
request(URL, options, function(err, res, body) {
if (err) {
console.log(err);
} else {
if (body.status === "ready") {
processReport(body);
// do any other processing here on the result
} else {
// try again in 20 seconds
setTimeout(checkForReportReady, 20*1000);
}
}
});
}
This will run one request, wait for the response, check the response, then if it's ready it will process it and if it's not ready, it will wait a period of time and then start another request. It will never have more than one request in-flight at the same time.
If you want to use Bluebird promises, you can do that also, though in this case it doesn't seem to change the complexity particularly much:
var request = Promise.promisifyAll(require('request'));
function checkForReportReady() {
return request(URL, options).spread(function(res, body) {
if (body.status === "ready") {
return body;
} else {
// try again in 20 seconds
return Promise.delay(20 * 1000).then(checkForReportReady);
}
});
}
checkForReportReady().then(function(body) {
processReport(body);
}, function(err) {
// error here
});

Categories