This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 4 years ago.
i am trying to create a function for rendering views in NodeJS that will be called using await later on.
const getTemplate = async (template, context) => {
app.render(template, { data: context }, (error, html) => {
return html;
});
}
const data = require('./data.json');
app.get('/pdf', async (req, res) => {
const html = await getTemplate('invoice', data);
res.send(html);
});
Right now it gives me an empty response and that's probably because it exits the getTemplate function before the render callback fires, but i don't know how to modify it in an elegant way.
You need to return a promise from getTemplate. Since you want to wait for the app.render to finish, I hope the following code does what you intend to do :)
// `async` is not really necessary as you are not using `await`, but you may provide it, if you want
const getTemplate = /* async */ (template, context) => {
return new Promise((resolve, reject) => {
app.render(template, { data: context }, (error, html) => {
if (error) return reject(error);
return resolve(html);
});
});
}
const data = require('./data.json');
app.get('/pdf', async (req, res) => {
const html = await getTemplate('invoice', data);
res.send(html);
});
Related
This question already has answers here:
await setTimeout is not synchronously waiting
(2 answers)
Closed last month.
I have a async fetch function that waits 2 seconds and returns a object:
async function fetch() {
var object;
await setTimeout(() => { object = { name: 'User', data: 'API Data' } }, 2000);
return object;
}
I want to display the object when the initialization is completely done (after 2 seconds)
fetch().then((val) => {
console.log("DONE!");
console.log(val.name);
}).catch((err) => {
console.log("ERROR!");
console.log(err);
});
The code prints both DONE and ERROR Cannot read properties of undefined (reading 'name')
I have tried with Promise, no luck
let promise = new Promise((resolve, reject) => {
let request = fetch();
if (request !== undefined)
resolve(request);
else
reject(request);
}).then((val) => {
console.log(val);
});
How can I properly check that fetch() has returned a value before printing without changing the inside of the function. I can delete the async and await in it but I am unable to edit it (I.E. adding a Promise inside)
Based on requirement
I can delete the async and await in it (fetch function) but I am unable to edit it (I.E. adding a Promise inside)
The only way I see is to override window.setTimeout function to make it to return a promise. That way you will be able to await it and there will be no need to modify your fetch function.
const oldTimeout = window.setTimeout;
window.setTimeout = (fn, ms) => {
return new Promise((resolve, reject) => {
oldTimeout(() => {
fn();
resolve();
}, ms);
});
};
async function fetch() {
var object;
await setTimeout(() => {
object = { name: "User", data: "API Data" };
}, 2000);
return object;
}
fetch()
.then((val) => {
console.log("DONE!");
console.log(val.name);
})
.catch((err) => {
console.log("ERROR!");
console.log(err);
});
NOTE: For anyone without this requirement - please, use other answers to this question or check await setTimeout is not synchronously waiting for additional details/explanations. This kind of overridings are very confusing due to everyone expect common and well-known functions to behavior in a way described in the docs.
You cannot await the setTimeout function, this is because your function returns undefined. You have used the promise in the wrong way. Below code will fix your issue.
function fetch() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: "User", data: "API Data" });
}, 2000);
});
}
fetch()
.then((val) => {
console.log("DONE!");
console.log(val.name);
})
.catch((err) => {
console.log("ERROR!");
console.log(err);
});
And remember that there is no need to change the setTimeout function.
The problem is that setTimeout does not actually return a promise, which means you cannot use await with setTimeout, that's why the var object; is returned instantly as undefined.
To solve this issue, you simply need to wrap setTimeout around a promise.
Like so:
function setTImeoutAwait(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
You can then use it like this:
async function fetch() {
var object;
await setTImeoutAwait(1000).then(() => {
object = { name: "test" };
});
return object;
}
This question already has answers here:
How do I promisify native XHR?
(6 answers)
Closed 1 year ago.
I am on the newer side to programming; the concept of promises and async/await functionality is something I have had a hard time wrapping my head around. But I know that it is something I should be utilizing in this case.
Background: Building a prototype-banking application with Node, Express, XML2js npm package to parse the XML data that I am working with, XMLhttpRequest, and EJS for templating.
I have this get route in my server.js:
const parseString = require('xml2js').parseString;
app.get('/home', (req, res) => {
makeCall(`<root>
<slipped>
<mail>
<alike>-1845676614.3625278</alike>
<paid>uncle</paid>
<kill>something</kill>
<name>Stephen<name>
<men>such</men>
<firm>rubbed</firm>
<using>yesterday</using>
</mail>
</slipped>
<pour>-1247721160</pour>
<poet>language</poet>
<sets>-1907281866</sets>
<treated>proper</treated>
<judge>781679047</judge>
</root>`)
//Putting the format of the XML *response* above, to show what I am rendering from
setTimeout(function () {
res.render('home.ejs', {
name: dataList[0].root.slipped.mail.name
})
}, 1000);
})
I am wanting to have the app wait and finish makeCall(), before home.ejs gets rendered. If I don't wait, then it will propagate the old value instead. It does work how it is now, but I believe there is a much more efficient way of doing this.
How can I rewrite the above logic using promises or async/await behavior instead of setTimeout?
For reference, the makeCall():
const makeCall = (call) => {
myRequest.open('POST', 'http://111.222.3.444:55555')
myRequest.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
myRequest.send(call)
myRequest.onload = () => {
if (myRequest.status === 200) {
parseString(myRequest.responseText, (err, result) => {
dataList.unshift(result)
})
} else {
console.log('Something went wrong, status code: ' + myRequest.status)
}
}
}
Thank you in advance for any help you can provide me :)
Return a promise in makeCall so you can wait for it in your main method. Here is an example
const makeCall = (call) => {
return new Promise((resolve, reject) => {
myRequest.open('POST', 'http://111.222.3.444:55555')
myRequest.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
myRequest.send(call)
myRequest.onload = () => {
if (myRequest.status === 200) {
parseString(myRequest.responseText, (err, result) => {
dataList.unshift(result);
resolve();
})
} else {
console.log('Something went wrong, status code: ' + myRequest.status)
reject();
}
}
});
}
Then you can wait for it to finish to continue in your main method. You can do that by using promises like:
makeCal(...)
.then(() => your_success_logic)
.catch((e) => your_error_logic);
Or you can use async/await like this:
app.get('/home', async (req, res) => {
await makeCal(...);
res.render('home.ejs', {
name: dataList[0].root.slipped.mail.name
});
});
You can make something asynchronous by using the new Promise constructor. It takes a function with two callbacks (resolve,reject). If it fails, reject it. If it's successful you can use resolve. Node 10 and above you can use the async function like so and use the await keyword to wait until a asynchronous function resolves or rejects before moving foward.
const parseString = require('xml2js').parseString;
app.get('/home', async (req, res) => {
await makeCall(`<root>
<slipped>
<mail>
<alike>-1845676614.3625278</alike>
<paid>uncle</paid>
<kill>something</kill>
<name>Stephen<name>
<men>such</men>
<firm>rubbed</firm>
<using>yesterday</using>
</mail>
</slipped>
<pour>-1247721160</pour>
<poet>language</poet>
<sets>-1907281866</sets>
<treated>proper</treated>
<judge>781679047</judge>
</root>`)
//Putting the format of the XML *response* above, to show what I am rendering from
res.render('home.ejs', {
name: dataList[0].root.slipped.mail.name
})
})
const makeCall = (call) => {
return new Promise((resolve, reject) => {
myRequest.open('POST', 'http://111.222.3.444:55555')
myRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
myRequest.send(call)
myRequest.onload = () => {
if (myRequest.status === 200) {
parseString(myRequest.responseText, (err, result) => {
if (err) {
reject(err)
} else {
dataList.unshift(result)
resolve()
}
})
} else {
console.log('Something went wrong, status code: ' + myRequest.status)
reject(new Error(''Something went wrong, status code: ' + myRequest.status'))
}
}
})
}
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 2 years ago.
I need to download all images and generate word document with them.
Using nodeJS and Meteor
WebApp.connectHandlers.use('/download', async function (req, res, next) {
// ...
const images = [];
await lines.forEach(async (line, k) => {
if (line.type && line.type === 'image') {
images.push({
id: line.id,
file: line.id + '.jpg',
});
download_image(line.imageUrl, line.id + '.jpg');
}
});
// ...
// Then I use images[] to insert them into a Word document.
});
const download_image = (url, image_path) =>
axios({
url,
responseType: 'stream',
}).then(
(response) =>
new Promise((resolve, reject) => {
response.data
.pipe(fs.createWriteStream(image_path))
.on('finish', () => resolve())
.on('error', (e) => reject(e));
})
);
The problem is images are not getting downloaded before I insert the into a Word document.
How to stop/await before images are finished to be downloaded? I am not so good with promises. What is missing her?
Thanks!
its common mistake to use .forEach (or similar array methods) with async function inside it. The async function just mean that it returns promise and the await works in a same way like chaining together promises with then. Therefore this line wait lines.forEach(async (line, k) => { will just create and return bunch of promises, but it will not wait for all the promises inside to finish.
WebApp.connectHandlers.use('/download', async function (req, res, next) {
// ...
const images = [];
const promises = [];
lines.forEach((line, k) => {
if (line.type && line.type === 'image') {
images.push({
id: line.id,
file: line.id + '.jpg',
});
promises.push(download_image(line.imageUrl, line.id + '.jpg'));
}
});
// here you get array with all the images downloaded
const downloadedImages = await Promise.all(promises);
// this line will be executed after you download all images
// ...
});
// This function would work same with or without the `async` keyword
// (because async function return promise - you are returning the promise.
// Async function allows to use await, but you are not using await in this function).
// However it is good practice to have all functions that returns promise
// marked as `async` so you know that you receive promise from it.
const download_image = async (url, image_path) =>
// Dont forget to return your promise otherwise you cannot await it
return axios({
url,
responseType: 'stream',
}).then(
(response) =>
new Promise((resolve, reject) => {
response.data
.pipe(fs.createWriteStream(image_path))
.on('finish', () => resolve())
.on('error', (e) => reject(e));
})
);
I am building a function in Firebase Cloud Functions, which can utilize Node.js modules.
I am still new to the use of .then() and I'm struggling to figure out a way to chain my 3 functions webhookSend(), emailSendgrid(), and removeSubmissionProcessor() that happen right after the 'count' is incremented (the if statement that checks temp_shouldSendWebhook). The whole idea of returning promises still confuses me a little, especially when it it involves external libraries.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const request = require('request');
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const SENDGRID_API_KEY = firebaseConfig.sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.submissionProcess = functions.database.ref('/submissions/processor/{submissionId}').onWrite((change, context) => {
var temp_metaSubmissionCount = 0; // omitted part of function correctly sets the count
var temp_shouldSendWebhook = true; // omitted part of function correctly sets the boolean
return admin.database().ref('/submissions/saved/'+'testuser'+'/'+'meta').child('count')
.set(temp_metaSubmissionCount + 1)
.then(() => {
// here is where im stuck
if (temp_shouldSendWebhook) {
webhookSend();
emailSendgrid();
removeSubmissionProcessor();
} else {
emailSendgrid();
removeSubmissionProcessor();
}
})
.catch(() => {
console.error("Error updating count")
});
});
function emailSendgrid() {
const user = 'test#example.com'
const name = 'Test name'
const msg = {
to: user,
from: 'hello#angularfirebase.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'your-template-id-1234',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: name
// and other custom properties here
}
};
return sgMail.send(msg)
}
function webhookSend() {
request.post(
{
url: 'URLHERE',
form: {test: "value"}
},
function (err, httpResponse, body) {
console.log('REQUEST RESPONSE', err, body);
}
);
}
function removeSubmissionProcessor() {
admin.database().ref('/submissions/processor').child('submissionkey').remove();
}
I want to be able to construct the 3 functions to be called one after another such that they will all execute.
In order to chain these functions, they each need to return a promise. When they do, you can call them sequentially like this:
return webhookSend()
.then(() => {
return emailSendgrid();
})
.then(() => {
return removeSubmissionProcessor();
});
Or in parallel like this:
return Promise.all([webhookSend, emailSendgrid, removeSubmissionProcessor]);
Now, to make your functions return promises:
emailSendgrid: It looks like this returns a promise (assuming sgMail.send(msg) returns a promise), so you shouldn't need to change this.
removeSubmissionProcessor: This calls a function that returns a promise, but doesn't return that promise. In other words it fires off an async call (admin.database....remove()) but doesn't wait for the response. If you add return before that call, this should work.
webhookSend calls a function that takes a callback, so you'll either need to use fetch (which is promise-based) instead of request, or you'll need to convert it to return a promise in order to chain it:
function webhookSend() {
return new Promise((resolve, reject) => {
request.post(
{
url: 'URLHERE',
form: {test: "value"}
},
function (err, httpResponse, body) {
console.log('REQUEST RESPONSE', err, body);
if (err) {
reject(err);
} else {
resolve(body);
}
}
);
});
}
Use async functions and then you can use .then() or await before every function calls
for reference read this
This question already has answers here:
How to use ES8 async/await with streams?
(7 answers)
Closed 4 years ago.
Hi I have async nodejs function where I am using stream concept. I want once stream is completed then I want to return from this function.
const removeMapping = async function (query) {
let stream = query.foreach();
stream.on('data', function (record) {
client.delete(record);
})
stream.on('error', function (error) {
})
stream.on('end', function () {
console.log("completed");
})
};
I am calling this function like this but after execution of this line, stream async code after this.
await mapping.deleteMapping(cookie);
Does anyone know how to handle this ?
Your function doesn't need to be async as you are not calling await within the function.
What you can do is return a new promise:
const removeMapping = function (query) {
return new Promise((resolve, reject) => {
let stream = query.foreach();
stream.on('data', function (record) {
client.delete(record);
})
stream.on('error', function (error) {
reject(error);
})
stream.on('end', function () {
resolve("completed");
})
})
};
You can then resolve or reject depending on what comes back from your stream.