How to wait until stream async is executed in nodejs [duplicate] - javascript

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.

Related

Multiple awaits in an async function does not return [duplicate]

This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 2 years ago.
I have the following code on a server:
let tmpFileName;
// GET
app.get('/clicked', async (req, res) => {
let nullOutput = writeTmpFile("hello, world!");
await deleteTmpFile();
console.log("Hurray, finished!");
res.send({result:nullOutput});
})
function writeTmpFile(content){
tmpFileName = "tmp" + Math.random().toString() + "tsl";
return new Promise(resolve => {
fs.writeFile(tmpFileName, content, function (err) {
if (err) throw err;
console.log('Temp file creation successful.');
});
})
}
function deleteTmpFile(spec){
return new Promise(resolve => {
fs.unlink(tmpFileName, function (err) {
if (err) throw err;
console.log('Temp file deletion successful.');
});
})
}
However, in my console output, I only get
Temp file creation successful.
Temp file deletion successful.
However, if I delete await deleteTempFile(), then Hurray, finished! shows up on the console.
And more generally, how do I debug these patterns of problems?
Why is this happening?
I have rewritten your code, to showcase how to use promises.
Promise callback gets two functions as arguments: resolve and reject.
You should call resolve when operation finishes with success, and reject when it fails.
// I moved `tmpFileName` variable from here into the request handler,
// because it was "global" and would be shared between requests.
app.get('/clicked', async (req, res) => {
let tmpFileName = "tmp" + Math.random().toString() + "tsl"
let writingResult = await writeTmpFile(tmpFileName, "hello, world!")
let deletionResult = await deleteTmpFile(tmpFileName)
res.send({ writingResult, deletionResult })
console.log("Hurray, finished!")
})
function writeTmpFile (filename, content) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, content, function (err) {
// on error reject promise with value of your choice
if (err) reject(err)
// on success resolve promise with value of your choice
resolve('Temp file creation successful.')
})
})
}
function deleteTmpFile (filename) {
return new Promise((resolve, reject) => {
fs.unlink(filename, function (err) {
if (err) reject(err)
resolve('Temp file deletion successful.')
})
})
}
For working with the file you can use writeFileSync instead writeFile. (Reference).
For multiple Promise you can use the Promise.all method.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
from MDN

Images are not getting downloaded using promises [duplicate]

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));
})
);

Creating a promise for rendering templates in NodeJS [duplicate]

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);
});

Get from URL data and convert to JSON array

I have this function
function getJsonObjectFromURL(url, onData) {
let chunks = [];
return require('https').get(url, res => {
res.setEncoding('utf8')
.on('data', (chunk) => {
chunks.push(chunk);
})
.on('end', () => {
onData(JSON.parse(chunks.join('')));
});
}).on('error', function(e) {
console.log("Got an error: ", e);
});
}
Also I have this script that converts url's data to json array.
url = https://pu.vk.com/c824502/upload.php?act=do_add&mid=213468131&aid=-14&gid=156603484&hash=7ab9a7e723425f4a6ca08709cbd5ebd0&rhash=ba8f0ec6580a6eafce38349b12ed3789&swfupload=1&api=1&wallphoto=1
getJsonObjectFromURL(url, data => {
console.log(data.server, data.photo, data.hash);
});
It goes well when console.log. But when I want to make from this script variable, it gives me huge collection
var xx = getJsonObjectFromURL(url, data => {
return data.server;
});
console.log(xx);
Your function getJsonObjectFromURL() doesn't return the object returned by the URL. It returns the object responsible for the https request code, which is something you don't want.
I see that you are using ES6, so the best solution for you is to probably create an async function that returns a promise, which will give you great flexibility. Here is an improved version of your code:
const https = require('https');
async function getJsonObjectFromURL(url) {
return new Promise((resolve, reject) => {
const chunks = [];
try {
https.get(url, res => {
res.setEncoding('utf8')
.on('data', (chunk) => {
chunks.push(chunk);
})
.on('end', () => {
resolve(JSON.parse(chunks.join('')));
});
}).on('error', e => reject(e));
} catch (err) {
reject(err);
}
});
};
This code allows you to retrieve the remote contents of the HTTPS url synchronously or asynchronously.
Asynchronous Call
As you have already done in your code, you can use a lambda callback that handles the response when it is ready.
const url = 'https://pu.vk.com/c824502/upload.php?act=do_add&mid=213468131&aid=-14&gid=156603484&hash=7ab9a7e723425f4a6ca08709cbd5ebd0&rhash=ba8f0ec6580a6eafce38349b12ed3789&swfupload=1&api=1&wallphoto=1';
// here we use a lambda callback that handles the response
getJsonObjectFromURL(url)
.then(data => {
console.log(data.server, data.photo, data.hash);
})
.catch(err => console.error(err));
Synchronous Call
The synchronous call forces the function to wait for the result. This is how you can do it:
async function getSync() {
try {
// wait for the result
const data = await getJsonObjectFromURL(url);
console.log(data.server);
} catch(err) {
console.error(err);
}
}
getSync();
Please note that we can only use the await keyword when we are inside an async function. This is why I had to wrap the synchronous call with a function.

Using Async inside another function [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I am using node.js and async with sails.js framework.
I am trying to create a function that perform some async DB operations on an array of data but I have problems figuring out a simple way to return the results of async to the parent function.
Here's my code:
convertProductfields: function (articlesFromAurelia){
async.each(articlesFromAurelia, function (post, cb) {
Categories.find({name: post.Categoria})
.then(function(category){
post.cat_id = category[0].cat_id;
cb();
})
.fail(function(error){
cb(error);
})
}, function(error){
if(error) return res.negotiate(error);
sails.log.debug('articlesFromAureliaModified ' , articlesFromAurelia);
return articlesFromAurelia;
});
sails.log.debug('articlesFromAureliaNotModified ' , articlesFromAurelia);
return articlesFromAurelia;
}
The problem of course is the execution order of the code. My function has already returned when the results of Async operations are available.... so, how to make it work? Thanks!!
Using Node 6.0, in built Promises can be used.
convertProductfields: function (articlesFromAurelia){
var allPromises = articlesFromAurelia
.map(post => new Promise((resolve, reject) => {
Categories.find({name: post.Categoria})
.then((category) => resolve(category))
.fail((error) => reject(error))
}));
return Promise.all(allPromises);
}
And to use the above function,
convertProductfields(articlesFromAurelia)
.then(() =>{
//handle success
}).catch(() => {
//handle error
})
Hope it helps you.
convertProductfields: function (articlesFromAurelia, callback){
async.each(articlesFromAurelia, function (post, cb) {
Categories.find({name: post.Categoria})
.then(function(category){
post.cat_id = category[0].cat_id;
cb();
})
.fail(function(error){
cb(error);
})
}, function(error){
if(error)
return callback(null); //incase of error, return null
sails.log.debug('articlesFromAureliaModified ' , articlesFromAurelia);
return callback(articlesFromAurelia); //return updated articles
});
}

Categories