Combine result of promises into a single array in JavaScript - javascript

I try to parse all the files that were uploaded as a directory upload into a single array of files. I have a problem that I cannot combine result of all the promises into a single array. I get a multidimensional array instead.
function iterateThroughUploadedFiles(files, isEntry = false) {
var promises = [];
for (let i = 0; i < files.length; i++) {
let entry = isEntry ? files[i].webkitGetAsEntry() : files[i];
if (entry.isFile) {
promises.push(new Promise(function(resolve, reject) {
getFile(entry, resolve, reject);
}));
}
else if (entry.isDirectory) {
let dirReader = entry.createReader();
var promise = new Promise(function(resolve, reject) {
readEntries(dirReader, resolve, reject);
});
promises = promises.concat(promise);
}
}
return Promise.all(promises);
}
function getFile(fileEntry, resolve, reject) {
fileEntry.file(function(file) {
resolve(file)
});
}
function readEntries(dirReader, resolve, reject) {
dirReader.readEntries(function (entries) {
resolve(iterateThroughEntries(entries).then(function(result) {
return result;
}));
});
}
Usage:
iterateThroughUploadedFiles(files, true).then(function(result) {
console.log(result);
});
When I execute iterateThroughUploadedFiles function the result variable is a multidimensional array. However, I want this array to be flattened (example: [File(6148), Array(1), Array(0), File(14)]). I am not very familiar with callbacks and Promises...therefore, I have some issues working with them.
Edit:
There is a typo I made inside readEntries(dirReader, resolve, reject) function. There should be iterateThroughUploadedFiles instead of iterateThroughEntries function.

return Promise.all(promises);
That resolves to a two dimensional array, which you can easily flatten by using some generator functions:
function* flatten(arr) {
for(const el of arr) {
if(Array.isArray(el)) {
yield* flatten(el);
} else {
yield el;
}
}
}
return Promise.all(promises).then(array => ([...flatten(array)]));
While passing resolve and reject works kind of, that can be done way more elegant with just returning a new Promise from the getFile function:
function getFile(fileEntry, resolve, reject) {
return new Promise(resolve => fileEntry.file(resolve));
}
Then it can be easily chained
function readEntries(dirReader) {
return new Promise(resolve => dirReader.readEntries(resolve))
.then(iterateThroughEntries);
}
function iterateThroughUploadedFiles(files, isEntry = false) {
return Promise.all( files.map( file => {
let entry = isEntry ? file.webkitGetAsEntry() : file;
if (entry.isFile) {
return getFile(entry);
} else if (entry.isDirectory) {
return readEntries( entry.createReader());
}
})).then(array => ([...flatten(array)]));
}

Related

Javascript execute function after Promise.all

I have a set of functions in a promise and after all of them have been executed I need to call a specific function that used the results obtained from each function. This is the code:
async function returnValues() {
var finalVal1;
function returnValue1() {
return new Promise((resolve, reject) => {
finalVal1 = {};
//do stuff
return finalVal1;
})
}
var finalVal2;
function returnValue2() {
return new Promise((resolve, reject) => {
finalVal2 = {};
//do stuff
return finalVal2;
})
}
var finalVal3;
function returnValue3() {
return new Promise((resolve, reject) => {
finalVal3 = {};
//do stuff
return finalVal3;
})
}
var finalVal4;
function returnValue4() {
return new Promise((resolve, reject) => {
finalVal4 = {};
//do stuff
return finalVal4;
})
}
let promise = await Promise.all([returnValue1(), returnValue2(), returnValue3(), returnValue4()]).then(myFunction(finalVal1, finalVal2, finalVal3, finalVal4));
}
function myFunction(finalVal1, finalVal2, finalVal3, finalVal4) {
console.log(finalVal1);
console.log(finalVal2);
console.log(finalVal3);
console.log(finalVal4);
}
The problem is that every returnVal() function is working perfectly fine, and the myFunction() work fine if I'm calling them separately, but when I'm using the Promise.all it never executes the myFunction() bit.
Where am I wrong?
Thank you very much
Try like this :
Simplified "function that returns a Promise" to a constant.
Promises do resolve()
The result of await Promise.all is an array of values, this array is passed to myFunction
async function returnValues() {
const returnValue1 = new Promise((resolve, reject) => {
let finalVal1 = {};
//do stuff
resolve(finalVal1);
});
const returnValue2 = new Promise((resolve, reject) => {
let finalVal2 = {};
//do stuff
resolve(finalVal2);
})
}
const values = await Promise.all([returnValue1, returnValue2])
myFunction(values);
}
function myFunction(values) {
console.log(values[0]);
console.log(values[1]);
}

Async and parallel function execution in JavaScript and Node.js

I'm currently using two approaches to parallelize the execution of async functions in JavaScript Node.js. I do not intend to find which of the Promise or callback approach is best, but I'm likely searching for a better approach to parallelize functions execution in Node.js without using child_process.fork(), so that it can be used in the same process.
Assumed that my functions are Promise, hence a typical function will be like
var myFunc = () => new Promise((resolve, reject) => {
setTimeout(() => resolve("some task"), 500)
})
myFunc().then(_ => console.log(_)).catch(err => console.err(err))
One approach is using Promise.all in this way:
var promiseAllP = function(items, block) {
var promises = [];
items.forEach(function(item, index) {
promises.push(function(item, i) {
return new Promise(function(resolve, reject) {
return block.apply(this, [item, index, resolve, reject]);
});
}(item, index))
});
return Promise.all(promises);
} //promiseAll
promiseAllP([...Array(3).keys()], (item, index, resolve, reject) => {
setTimeout(() => resolve("task "+index), 500 * index)
})
.then(_ => console.log(_)).catch(err => console.err(err))
the other approach would be using async execution with a callback like
var tasks = [
(cb) => setTimeout(() => cb(null, "task 1"), 500 * 1),
(cb) => setTimeout(() => cb(null, "task 2"), 500 * 2),
(cb) => setTimeout(() => cb(null, "task 3"), 500 * 3),
];
function parallel(tasks, cb) {
var results, pending, keys
var isSync = true
if (Array.isArray(tasks)) {
results = []
pending = tasks.length
} else {
keys = Object.keys(tasks)
results = {}
pending = keys.length
}
function done(err) {
function end() {
if (cb) cb(err, results)
cb = null
}
if (isSync) process.nextTick(end)
else end()
}
function each(i, err, result) {
results[i] = result
if (--pending === 0 || err) {
done(err)
}
}
if (!pending) {
// empty
done(null)
} else if (keys) {
// object
keys.forEach(function(key) {
tasks[key](function(err, result) {
each(key, err, result)
})
})
} else {
// array
tasks.forEach(function(task, i) {
task(function(err, result) {
each(i, err, result)
})
})
}
isSync = false
}
parallel(tasks, (err, res) => {
if (err) console.error(error)
else console.log(res)
});
The last one can be easily adapted to support Promise instead of callback. The problem is that I didn't find any improvement in executing my tasks through the first approach or the second one, so which is the best solution among them or is out of there a better approach to execute parallel tasks without using Node.js child_process.fork()?
The tasks to run have different execution times, they do not share memory, and I can run a callback or Promise.resolve to notify the caller that the task ended gracefully or Promise.reject if some error occurred.

How to execute functions sequentially using Node Js Promise?

I'm new to Node js Promise I'm not sure whether I'm using the Promise correctly or not so here is my code.
function print(){
first('first')
.then(second('second'))
.then(third('third'));
}
function first(a){
return new Promise((resolve, reject) => {
var res1 = function (){
resolve(a);
}
});
console.log(a);
}
function second(b){
return new Promise((resolve, reject) => {
var res1 = function (){
resolve(b);
}
});
setTimeout(() => {
console.log(b);
}, 2000);
}
function third(c){
return new Promise((resolve, reject) => {
var res1 = function (){
resolve(c);
}
});
console.log(c);
}
My desired output is
first
second
third
Instead what I get is
first
third
//after two seconds
second
I'm missing something but I can't figure it out please explain me
To get the expected behaviour, you need to resolve inside of the timeout (next to the console log). You also cannot pass arguments into promise chain functions since they need to accept the promise from the previous thennable.
A working snippet is below:
print();
function print(){
first('first')
.then(second)
.then(third);
}
function first(a){
return new Promise((resolve, reject) => {
console.log(a);
resolve(a);
});
}
function second(b){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("second");
resolve(b);
}, 2000);
});
}
function third(c){
return new Promise((resolve, reject) => {
console.log("third");
resolve(c)
});
}

Promise.all is not displaying the data

I am new to promises and I want to return the data from classes as promises. I have two classes with two function in each class. What I am doing each function is returning a promise from a class, but I am unable to execute promise.all. Below is the code
Class1
class TestClass {
firstPromise() {
return new Promise((resolve, reject) => {
resolve('return first promise')
})
}
secondPromise() {
return new Promise((resolve, reject) => {
resolve('return second promise')
})
}
}
module.exports.TestClass = TestClass;
Class2
class TestClass1 {
firstPromise() {
return new Promise((resolve, reject) => {
resolve('return first promise')
})
}
secondPromise() {
return new Promise((resolve, reject) => {
resolve('return second promise')
})
}
}
module.exports.TestClass1 = TestClass1;
Main function
let data = req.body;
let test1Object;
let testObject;
let testParam;
let testParam1;
if (data.hasOwnProperty('param1')) {
test1Object = new test1.TestClass1();
test1Object.firstPromise().then((data)=>{
testParam1 = test1Object.secondPromise();
});
}
if (data.hasOwnProperty('param2')) {
testObject = new test.TestClass();
testObject.firstPromise().then((data)=>{
testParam = testObject.secondPromise()
});
}
Promise.all([testParam,testParam1]).then((data)=>{
console.log(data)
});
it displays
[ undefined, undefined ]
When you're executing Promise.all(), the promises are not yet resolved, so testParam and testParam1 are undefined. I think you should assign the first promise to testParam1:
testParam1 = test1Object.firstPromise().then(data => test1Object.secondPromise());
and similarly the second to testParam:
testParam = testObject.firstPromise().then(data => testObject.secondPromise());

Why Promise.all cannot work after all asynchronous function are completed?

Hi I'm new to Js and I'd like to wait some async functions complete before print ni. But the code never print it I cannot understand why. Please help me:(
// public function
this.run = function() {
'use strict';
let compile_lib = lib_array.map((item) => {
return new Promise(() => {compileEntry(item);})
});
Promise.all(compile_lib).then(() => { console.log("ni"); });
}
The Promise.all(iterable) method returns a promise that resolves when
all of the promises in the iterable argument have resolved, or rejects
with the reason of the first passed promise that rejects.
In your example, you didn't resolve or reject your Promises:
let compile_lib = lib_array.map((item) => {
return new Promise((resolve, reject) => { compileEntry(item); })
});
please note the arguments resolve, reject. You should use these callbacks either to resolve or reject the promise. For example:
new Promise((resolve, reject) => {
var result = compileEntry(item);
if(result) {
resolve(result);
}else {
reject("reson");
}
})
You need to use resolve and reject
new Promise( /* executor */ function(resolve, reject) { ... } );
var lib_array = [1, 2, 3];
function compileEntry(item, index) {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(console.log(item));
}, index * 1000);
});
}
this.run = function() {
'use strict';
let compile_lib = lib_array.map((item, index) => {
return compileEntry(item, index);
});
Promise.all(compile_lib).then(() => {
console.log("ni");
});
}
this.run();

Categories