A loop stops the execution of a route on Express JS until it finishes, this can generate a timeout in the browser.
In the example it takes approx. 10 seconds to show the website (I need to execute the sleep function) , but I would like this for you to continue running on Background, and the route will be displayed immediately
Any suggestion?
app.get('/', function(req, res) {
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let array = [1,2,3,4,5,6,7,8,9]
for (const a of array) {
console.log(a)
await sleep(1000);
}
res.send('hello world');
});
If you want the response send immediately, then put the res.send() BEFORE your loop. You can still run code after you've sent the response - that code just can't be involved in computing what the response will be because it will have already been sent.
app.get('/', function(req, res) {
res.send('hello world');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let array = [1,2,3,4,5,6,7,8,9]
for (const a of array) {
console.log(a)
await sleep(1000);
}
});
Remove the await keyword in the for loop. The await keywords basically stops the code from executing until the current Promise is resolved, so if you dont await on it, it runs alongside other pieces of code. Your response is returned in less than a second, while the node process resolves the sleep promise in the background
app.get('/', function(req, res) {
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let array = [1,2,3,4,5,6,7,8,9]
for (const a of array) {
console.log(a)
// await sleep(1000);
sleep(1000);
}
res.send('hello world');
});
Related
There are other questions similar to this one but not a single one of them helped me visualize my mistake.
I have this code:
function calc() {
return new Promise(resolve => {
setTimeout(() => {
resolve('block finished');
}, 5000);
});
}
async function asyncBlock() {
let result = await calc();
console.log('Result: ' + result);
return result;
}
app.get('/block', (req, res) => {
let result = asyncBlock();
console.log('Already returned the response without the result.');
res.send(result);
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
The execution continues without the await response, giving me this output:
Example app listening on port 3000
Already returned the response without the result.
Result: block finished
Mozilla documentations states that
If a Promise is passed to an await expression, it waits for the
Promise to be fulfilled and returns the fulfilled value.
Mozilla Doc
Your call to AsyncBlock isn't asynchronous.
Try this:
app.get('/block', async (req, res) => {
let result = await asyncBlock();
console.log('Waited for the result');
res.send(result);
})
I've been having an issue with awaiting a function in a for loop in Nodejs
Initially I believed that I was missing something in the stream docs but then rewrote my code to not use streams and still have the same issue
It seems as though using await in a for loop is causing the loop to wait once till the promise resolves then exits the loop
here's an example of my issue: it uses streams as its simpler but I can post a longer example without them if needed
const { PassThrough } = require("stream");
const waitTillStreamEnd = (stream) => {
console.log("waiting for end");
const prom = new Promise((res) => {
stream.on("end", res);
});
return prom.then((e) => {
console.log(e);
return e;
});
};
(async () => {
for (let i = 0; i < 10; i++) {
const stream = new PassThrough();
setTimeout(() => stream.end("test"), 3000);
console.log("timeout set");
const res = await waitTillStreamEnd(stream);
console.log("after await");
}
})();
my output from this is:
timeout set
waiting for end
Nothing after the promise resolves is executed, removing the await causes the loop to run 10 times as I'd expect but it only ones once with the await statement there
Any help would be appreciated
It looks like the promise that you set up in waitTillStreamEnd is not being resolved, which means your loop is not going to get past the first execution.
I note that in waitTillStreamEnd you are returning a promise.then()
However, you should just return the promise, so that it can be properly resolved by the await call in your loop.
Also, with this code:
then((e) => {
console.log(e);
return e;
});
Looks like you are trying to do some error handling with this, but it's not working.
A better place to do error handling would be by providing a second reject callback when setting up your promise, and then listening for the stream error event. This would then cause the error to be propagated through to your for loop for proper handling via a try catch block.
Something along these lines, just typed this on the fly, so there may be some formatting issues :
const { PassThrough } = require("stream");
const waitTillStreamEnd = (stream) => {
console.log("waiting for end");
const prom = new Promise((resolve, reject) => {
stream.on("end", resolve);
stream.on("error", err=>{
reject(err) });
});
return prom;
};
(async () => {
try {
for (let i = 0; i < 10; i++) {
const stream = new PassThrough();
setTimeout(() => stream.end("test"), 3000);
console.log("timeout set");
const res = await waitTillStreamEnd(stream);
console.log("after await");
}
} catch(err) {
console.error(err);
}
})();
Good luck !
Getting an exception in my Node JS Express application and cannot figure out why. I was getting the same error with my "real" code, so I found this code online and made a test router to see if the error occurred with known good code. Code runs fine in plnkr without the router.post line. Could this be because of the function in the first line? Any help is greatly appreciated.
router.post('/addTableTest', (function (req, res, next) {
let promise1 = new Promise((resolve, reject) => {
let data = false;
if (data) {
resolve('Data');
}
if (!data) {
reject('Not Data');
}
})
promise1.then((message) => {
console.log(message);
}).catch((message) => {
console.log(message);
})
}));
The closure executed by new Promise() is executed synchronously, i.e. immediately after the Promise has been created and before new returns. As your closure has been written to fail immediately and you can't attach a .catch() to it before new returns, you get an unhandled Promise rejection exception.
To make your code work you need to
start a Promise chain by creating a resolved Promise
attach a .then() clause to wrap your synchronous code
replace resolve(X) with return X
replace reject(X) with throw new Error(X)
Now you can safely attach the other Promise clauses, because the code in the just created Promise chain won't be executed until the closure that has created it leaves.
router.post('/addTableTest', (req, res, next) => {
let promise1 = Promise.resolve()
.then(() => {
let data = false; // i.e. the promise will reject
if (data) {
return 'Data';
} else {
throw new Error('Not Data');
}
});
promise1
.then(message => {
console.log(message);
})
.catch(error => {
console.log(error.message);
});
});
I have the following Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
await all({'p1': p1, 'p2': p2}).then((response) => {
...
console.log("Response:",
ruleCtrl.manageRule(detection, res);
});
});
ruleCtrl.manageRuleis as follows:
export async function manageRule(identifierDetected, res) {
let rule = db.getRule(identifierDetected);
await all([rule]).then((ruleExtracted) => {
...
res.json(ruleExtracted);
}).catch((err) => {
res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
});
}
and db.getRule:
export async function getRule(idRule) {
return new Promise((resolve, reject) => {
Rule.findOne({ruleID: idRule}, (err, rule) => {
if (err) {
reject("MongoDB Rule error: " + err);
} else {
resolve(rule);
}
});
})
}
My response is into manageRule and this function depends of the values extracted into the await all. So, right now, Express is returning a response before get the information from mongoose database (db).
Which is the way to handle this issue?
Thanks everyone!
I would refactor your code a bit to make it easier to read, and also return the result from ruleCtrl.manageRule(detection, res);.
The request might simply be timing out since your original code is missing a return there or an await (to make sure it finishes executing)
Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
// Catch any exceptions from the promises. This is the same as using .catch
try {
// Lets assign the returned responses to variable
let [p1Result, p2Result] = await all({'p1': p1, 'p2': p2});
...
console.log("Responses:", p1Result, p2Result);
// return the response from manageRule method
return ruleCtrl.manageRule(detection, res);
} catch(err) {
// Handle err here
}
});
One of the great benefits with async await is moving away from chained promises, so simply return the result from the await to a variable instead of using .then()
ruleCtrl.manageRule
export async function manageRule(identifierDetected, res) {
// Use try catch here to catch error from db.getRule. Assign to variable and return
// res.json
try {
let ruleExtracted = await db.getRule(identifierDetected);
...
return res.json(ruleExtracted);
} catch(err) {
return res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
}
}
You dont have to return res.json or res.status here, I just like to keep track of when I want to end function execution.
You could refactor the ruleCtrl.manageRule method even further by not sending in res as a parameter but by returning the result from db.getRule instead. Let router.post('/verify) handle req and res, so to make it even easier to read.
My problem is I'm trying to await on some promises, but my await call seems to return to the calling function right away.
In the code below (part of a controller function handling a POST), the "files coming out" log after my function call is executed prior to the promises being executed. My async function seemingly returns at the "await" call.
However the "await" still does wait for all the promises to execute, so the "I just finished waiting" log does have the magic var inserted into the req.files correctly.
const upload = multer({
storage: multer.diskStorage ({
destination: (req, file, cb) => { cb(null, config.DESTINATION) }
})
}).array(config.FIELD_NAME);
exports.upload_image = function(req, res) {
upload(req, res, (err) => {
if (err) {
return res.end("error uploading file");
}
// got file ok, do validation
checkAreDicom2( req.files );
console.log("files coming out: ", req.files);
return res.end("OK");
});
}
async function checkAreDicom2(inFiles) {
let promises = inFiles.map(function (file) {
return new Promise( function (resolve, reject) {
fs.createReadStream(file.path,{start: 128, end: 131, autoClose: true})
.on('error',reject)
.on('data', (chunk) => file.magic = file.magic ? file.magic += chunk : chunk)
.on('end', resolve);
});
});
await Promise.all(promises);
console.log("i just finished waiting: ", inFiles);
return;
}
The await is waiting for the Promise.all, and the log after it is correctly delayed. However it does of course not block the caller of function.
So the callback in upload_image does not wait for the asynchronous processing, it just receives the promise that checkAreDicom2(…) returns - and discards it, and immediately logs something. You would need to await the asynchronous result explicitly here again.
exports.upload_image = async function(req, res) {
try {
await new Promise((resolve, reject) => {
upload(req, res, (err) => {
if (err) reject(err);
else resolve();
});
});
// got file ok, do validation
await checkAreDicom2( req.files );
// ^^^^^
console.log("files coming out: ", req.files);
return res.end("OK");
} catch(err) {
return res.end("error uploading file");
}
};