Writing to file in a promise - javascript

The function here creates a bunch of files but does not write to them. How ever, if I remove the Promise.all in the end and don't resolve the function at all it DOES write the data to the files. It doesn't matter what I try to write, I can comment out everything and just write 'hello world' and it still won't write anything. The question is simple, why?
const writeSmilFiles = (smilInfo) => {
return new Promise ((resolve, reject) => {
const p1 = smilPartOne();
const p2 = smilPartTwo();
let promises = dataTypes.new.camera_set.map(instance => {
return new Promise((resolve, reject) => {
const smilEntries = smilInfo.filter(smil => smil.BroadcastAndKlippTupleId == instance.BroadcastAndKlippTupleId && smil.CameraId == instance.CameraId);
try {
const fileName = `${__dirname}/newSmilFiles/${smilEntries[0].Smil}`;
const file = fs.createWriteStream(fileName);
file.write(p1);
smilEntries.forEach(entry => {
const smilEntry = smilSwitch(entry.Filename, entry.Bitrate, entry.Width, entry.Height);
file.write(smilEntry);
console.log(smilEntry);
file.write('\n');
});
file.write(p2);
file.end();
resolve(`Smil written.`);
} catch (ex) {
reject(ex);
}
});
});
Promise.all(promises).then(msg => {
resolve(msg);
});
});
};

Resolve when the stream has actually finished:
file.on("finish", () => resolve(`Smil written.`));

Related

Chained Promises not fully resolving on await

I have a function that reads files in a directory asynchronously (readdir) and filters for csv files. I also have an async function that calls readdir filtered for csv files and then iterates through them with fast-csv. Logging to the console the list and its length within the .on('end') function, I can see that they produce the desired results. however, my async call only resolves the first iteration.
const fs = require(`fs`);
const path = require(`path`);
const csv = require(`fast-csv`);
var ofsActivities = [];
const currDir = path.join(__dirname + `/../Downloads/`);
const readdir = async dirname => {
return new Promise((resolve, reject) => {
fs.readdir(dirname, (error, filenames) => {
error ? reject(error) : resolve(filenames);
});
});
};
const filtercsvFiles = (filename) => {
return filename.split(`.`)[1] == `csv`;
};
const ofsDataObjectArray = async () => {
return readdir(currDir).then(async filenames => {
return await new Promise((resolve, reject) => {
filenames = filenames.filter(filtercsvFiles);
for (let i = 0; i < filenames.length; i++) {
let currFilePath = currDir + filenames[i];
console.log(`Reading File: ${filenames[i]}`);
csv
.parseFile(currFilePath)
.on(`data`, (data) => {
//Doing stuff
})
.on(`error`, error => reject(error))
.on(`end`, () => resolve(ofsActivities)); //Inserting a console.log(ofsActivities.length) logs the correct and expected length on the last iteration
}
});
});
};
(async () => {
let list = await ofsDataObjectArray(); // This seems to only resolve the first iteration within the promise
console.log(list.length);
})();
You need to call resolve() only when the LAST csv.parseFile() is done. You're calling it when the FIRST one is done, thus the promise doesn't wait for all the others to complete. I'd suggest you promisify csv.parseFile() by itself and then await that inside the loop or accumulate all the promises from csv.parseFile() and use Promise.all() with all of them.
Here's using await on each csv.parseFile():
const ofsDataObjectArray = async () => {
return readdir(currDir).then(async filenames => {
filenames = filenames.filter(filtercsvFiles);
for (let i = 0; i < filenames.length; i++) {
let currFilePath = currDir + filenames[i];
console.log(`Reading File: ${filenames[i]}`);
await new Promise((resolve, reject) => {
csv.parseFile(currFilePath)
.on(`data`, (data) => {
//Doing stuff
})
.on(`error`, reject)
.on(`end`, () => resolve(ofsActivities));
});
}
return ofsActivities;
});
};
Or, here's running them in parallel with Promise.all():
const ofsDataObjectArray = async () => {
return readdir(currDir).then(filenames => {
filenames = filenames.filter(filtercsvFiles);
return Promise.all(filenames.map(file => {
let currFilePath = currDir + file;
console.log(`Reading File: ${file}`);
return new Promise((resolve, reject) => {
csv.parseFile(currFilePath)
.on(`data`, (data) => {
//Doing stuff
})
.on(`error`, error => reject(error))
.on(`end`, () => resolve(ofsActivities));
});
}))
});
};
P.S. It's unclear from your question what final result you're trying to accumulate (you have left that out) so you will have to add that to this code in the "doing stuff" code or by modifying the resolve(something) code.

How to call HttpException in setTimeout, when i wait promise a long time?

I get products from different servers, I want return exception, when wait a long time.
I have created setTimeout for this, but it stoped server and didn't return error.
How to fix it?
const server = 23;
const productsTimeout = setTimeout(() => { throw new HttpException('Problem', HttpStatus.INTERNAL_SERVER_ERROR) }, 3000);
products = await new Promise((resolve, reject) => {
this.socketClientCabinet.on('products_get', async ({ server, products }) => {
if (server === serverID) {
const {
productsSymbols,
} = this.productsTransform(products);
clearTimeout(productsTimeout);
resolve(productsSymbols);
}
});
});
User delete his answer, I have fixed it.
let productsTimeout;
const timeoutPromise = new Promise((res, rej) => {
instrumentsTimeout = setTimeout(() => rej(new HttpException('problem', 500)), 1000, )
})
const productsPromise = new Promise((resolve, reject) => {
this.socketClientCabinet.on('products_get', async ({ server, products }) => {
if (server === serverID) {
const {
productsSymbols,
} = this.productsTransform(products);
clearTimeout(productsTimeout);
resolve(productsSymbols);
}
});
});
const products = await Promise.race([
timeoutPromise,
productsPromise
])
It is work.

Calling other functions of a class inside a Promise in Node

So, I have two methods in a class. Both returns a promise. The second function calls the first function from inside of the promise it returns.
module.exports = {
funcA: () => {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: () => {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
When I am trying to call funcB() from another class, like this:
let something = await someService.funcB();
I am getting:
TypeError: this.funcA() is not a function
Can you shed some light on why this is happening and how to solve this problem?
one way to make it work is to create the function outside of the module.exports block to get a reference of each function. Then this keyword can be omitted
const funcA = () => {
return new Promise((resolve, reject) => {
// code here
});
};
const funcB = () => {
return new Promise(async(resolve, reject) => {
try {
const something = await funcA();
resolve(something);
} catch(err) {
reject('error');
}
})
};
module.exports = {
funcA,
funcB
}
I think this is what you need to do
module.exports = {
funcA: function() {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: function() {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
I've found using arrow functions inside objects as you've done breaks this, but you can fix it this way.

Promise .then() chains: second .then run before the first?? o.0

I'm trying to create a new File object from blob data in a .then structure, and after in the second .then, read info of this file.
But the second then run before the first end, so the file object isn't filled yet.
Is that a normal behavior? Should I make an async function, called in the first then to ensure the second one is strictly called after?
let output = {file: {}, file_infos: {}},
image = FileAPI.Image(src_file);
await Promise.all(Object.keys(parameters).map(async (parameter_name) => { // Pass file threw all modifiers (resizing, rotation, overlaying)
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
}
catch(err) {
console.log(err);
};
}))
.then((output_image) => {
output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
});
})
.then(() => {
console.log('step2');
FileAPI.getInfo(output.file, (err/**String*/, infos/**Object*/) => {
if( !err ){
output.file_infos = infos;
} else {
console.log("this is triggered because output.file isn't filled yet");
}
})
});
// console.log(output);
return output;
console shows me:
step2
this is triggered because output.file isn't filled yet
step1
Thanks for helps :)
The two asynchronous functions in the two .then's do not return a Promise, so first they need to be "Promisified", also, since you're already using async/await don't use a promise .then chain
const image = FileAPI.Image(src_file);
const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
} catch (err) {
console.log(err);
};
}));
const file = await new Promise((resolve, reject) => output_image[0].toBlob((blob) =>
resolve(new File([blob], src_file.name))
));
const file_infos = await new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
if (!err) {
resolve(file_infos);
} else {
reject("this is triggered because output.file isn't filled yet");
}
));
return {file, file_infos};
A note about
const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
try {
image = await FileMethods[parameter_name](image, parameters[parameter_name]);
return image;
} catch (err) {
console.log(err);
};
}));
you're essentially doing return await FileMethods[parameter_name](image, parameters[parameter_name]) - so, you really don't need in this case an async/await pattern, just return the Promise in the .map
const output_image = await Promise.all(Object.keys(parameters).map((parameter_name) =>
FileMethods[parameter_name](image, parameters[parameter_name]);
));
Or, even nicer (in my opinion)
const output_image = await Promise.all(Object.entries(parameters).map((p_name, p_value) =>
FileMethods[p_name](image, p_value)
));
Alternatively, to use Promise .then chains and no async/await
const image = FileAPI.Image(src_file);
return Promise.all(Object.keys(parameters).map(parameter_name => FileMethods[parameter_name](image, parameters[parameter_name])))
.then(output_image => new Promise((resolve, reject) => output_image[0].toBlob((blob) =>
resolve(new File([blob], src_file.name))
)))
.then(file => new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
if (!err) {
resolve({file, file_infos});
} else {
reject("this is triggered because output.file isn't filled yet");
}
)));
.toBlob() returns instantly because it uses the asynchronous callback pattern.
What you want is to return a promise that resolves when the work is done. So you could do something like this:
.then((output_image) => {
return new Promise((res, rej) => {
output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
res();
});
});
})
toBlob is probably asynchronous. Change the first .then to this:
.then((output_image) => {
return new Promise((resolve) => output_image[0].toBlob((blob) => {
output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
console.log('step1');
resolve();
}));
})

waiting for many async functions execution

I have the promise function that execute async function in the loop few times for different data. I want to wait till all async functions will be executed and then resolve(), (or call callback function in non-promise function):
var readFiles = ()=>{
return new Promise((resolve,reject)=>{
var iterator = 0;
var contents = {};
for(let i in this.files){
iterator++;
let p = path.resolve(this.componentPath,this.files[i]);
fs.readFile(p,{encoding:'utf8'},(err,data)=>{
if(err){
reject(`Could not read ${this.files[i]} file.`);
} else {
contents[this.files[i]] = data;
iterator--;
if(!iterator) resolve(contents);
}
});
}
if(!iterator) resolve(contents); //in case of !this.files.length
});
};
I increase iterator on every loop repetition, then, in async function's callback decrease iterator and check if all async functions are done (iterator===0), if so - call resolve().
It works great, but seems not elegant and readable. Do you know any better way for this issue?
Following up the comment with some code and more detail!
Promise.all() takes an iterator, and waits for all promises to either resolve or reject. It will then return the results of all the promises. So instead of keeping track of when all promises resolve, we can create little promises and add them to an array. Then, use Promise.all() to wait for all of them to resolve.
const readFiles = () => {
const promises = [];
for(let i in files) {
const p = path.resolve(componentPath, files[i]);
promises.push(new Promise((resolve, reject) => {
fs.readFile(p, {encoding:'utf8'}, (err, data) => {
if(err) {
reject(`Could not read ${files[i]} file.`);
} else {
resolve(data);
}
});
}));
}
return Promise.all(promises);
};
const fileContents = readFiles().then(contents => {
console.log(contents)
})
.catch(err => console.error(err));
You only need push all the Promises into an array to then pass it as argument to Promise.all(arrayOfPromises)
try something like this:
var readFiles = () => {
var promises = [];
let contents = {};
var keys_files = Object.keys(this.files);
if (keys_files.length <= 0) {
var promise = new Promise((resolve, reject) => {
resolve(contents);
});
promises.push(promise);
}
keys_files.forEach((key) => {
var file = this.files[key];
var promise = new Promise((resolve, reject) => {
const currentPath = path.resolve(this.componentPath, file);
fs.readFile(p,{encoding:'utf8'},(err, data) => {
if (err) {
return reject(`Could not read ${file} file.`);
}
contents[file] = data;
resolve(contents)
});
});
});
return Promises.all(promises);
}
Then you should use the function like so:
// this will return a promise that contains an array of promises
var readAllFiles = readFiles();
// the then block only will execute if all promises were resolved if one of them were reject so all the process was rejected automatically
readAllFiles.then((promises) => {
promises.forEach((respond) => {
console.log(respond);
});
}).catch((error) => error);
If you don't care if one of the promises was rejected, maybe you should do the following
var readFiles = () => {
var promises = [];
let contents = {};
var keys_files = Object.keys(this.files);
if (keys_files.length <= 0) {
var promise = new Promise((resolve, reject) => {
resolve(contents);
});
promises.push(promise);
}
keys_files.forEach((key) => {
var file = this.files[key];
var promise = new Promise((resolve, reject) => {
const currentPath = path.resolve(this.componentPath, file);
fs.readFile(p,{encoding:'utf8'},(err, data) => {
// create an object with the information
let info = { completed: true };
if (err) {
info.completed = false;
info.error = err;
return resolve(info);
}
info.data = data;
contents[file] = info;
resolve(contents)
});
});
});
return Promises.all(promises);
}
Copied from comments:
Also - you might want to use fs-extra, a drop-in replacement for fs, but with promise support added.
Here's how that goes:
const fs = require('fs-extra');
var readFiles = ()=>{
let promises = files
.map(file => path.resolve(componentPath, file))
.map(path => fs.readFile(path));
return Promise.all(promises);
});
Nice and clean. You can then get contents like this:
readFiles()
.then(contents => { ... })
.catch(error => { ... });
This will fail on first error though (because that's what Promise.all does). If you want individual error handling, you can add another map line:
.map(promise => promise.catch(err => err));
Then you can filter the results:
let errors = contents.filter(content => content instanceof Error)
let successes = contents.filter(content => !(content instanceof Error))

Categories

Resources