How to rewrite a function using Promises.all to async and await - javascript

I have a function that takes an array of URLs and downloads them. It looks like this:
const loadFiles = filePaths => {
return new Promise((resolve, reject) => {
let fetchPromises = filePaths.map(filePath => fetch(filePath))
Promise.all(fetchPromises).then(fetchResolves => {
let textPromises = []
fetchResolves.forEach(fetchResolve => {
if (!fetchResolve.ok) {
return reject(`${fetchResolve.statusText} : ${unescape(fetchResolve.url)}`)
}
textPromises.push(fetchResolve.text())
})
Promise.all(textPromises).then(resolve)
})
})
}
export {loadFiles}
The issue I am having is that the multiple calls to Promise.all start to resemble callbacks even though I am using promises.
Is there a way to map the functionality of Promise.all to async and await to simplify this function?
How do I do this?

You can await Promise.all just fine. Example:
async function loadFiles(filePaths) {
let fetchPromises = filePaths.map(filePath => fetch(filePath))
let fetchResolves = await Promise.all(fetchPromises)
let textPromises = []
fetchResolves.forEach(fetchResolve => {
if (!fetchResolve.ok) {
throw new Error(`${fetchResolve.statusText} : ${unescape(fetchResolve.url)}`)
}
textPromises.push(fetchResolve.text())
})
return Promise.all(textPromises)
}

Related

Trying to pass array and use foreach to send back multiple data

I had getProductInfo orgianlly, as two parameters, where it would be (res, sku). but now I want to pass a set object with sku numbers and for-each res.send the data
const activeProductBank = new Set([6401728, 6430161, 6359222, 6368084]);
getProductInfo = (res) => {
activeProductBank.forEach((SKU) => {
bby.products(SKU, { show:'sku,name' })
.then(function(data) {
res.send(data);
});
})
};
also tried this
getProductInfo = (res) => {
const allProductInfo = '';
activeProductBank.forEach((SKU) => {
bby.products(SKU, { show:'sku,name'})
.then(function(data) {
allProductInfo.concat(data);
});
})
res.send(allProductInfo);
};
The error I get "app listening at http://localhost:3000
(node:25556) UnhandledPromiseRejectionWarning: Error: Exceeded max retries"
You can use a combination of ASYNC / AWAIT and Promise.all to populate the allProductInfo as expected.
The caveat with ASYNC / AWAIT is that you can only use ASYNC function inside an ASYNC function. More about it here https://javascript.info/async-await
activeProductBank.map will iterate over all your activeProductBank and returns an array of Promises which are then passed over to the Promise.all which then resolves after all the promises in the list are reolved.
Promise.all
getProductInfo = async (res) => {
const allProductInfo = Promise.all(
activeProductBank.map(SKU => bby.products(SKU, { show:'sku,name'}))
)
res.send(allProductInfo);
};
Another approach is to use for..of loop and pushing the response of each productInfo one by one using the Await call like below
getProductInfo = async (res) => {
let allProductInfo = [];
for(let sku of allProductInfo) {
const productInfo = await bby.products(sku, { show:'sku,name'});
allProductInfo.push(productInfo);
}
res.send(allProductInfo);
};

writeFile does not wait for variable to be instantiated

I'm new to node.js and javascript in general but I am having issues understanding why the writeFile function is writing a blank file. I think the for loop should be a Promise but I am not sure how to write it.
const removeProviders = function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = '';
for (let providerId in providerArray) {
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
fs.writeFile('C:/path/to/file.txt', importFile);
You can collect all the promises from the for-loop into an Array and then pass them into Promise.all() which will resolve only after all of them resolved. If one of the promises are rejected, it will reject as well:
const promises = providerArray.map((item) => {
return getProvider(item)
.then((response) => {
let providerInfo = response.data;
return providerInfo;
})
.then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
});
});
Promise.all(promises).then(() => {
fs.writeFile('C:/path/to/file.txt', importFile, err => {
if (err) {
console.error(err);
}
});
});
While doing this you could also get rid of the importFile variable and collect directly the results of your promises. Promise.all().then(results => {}) will then give you an array of all results. Thus no need for an updatable variable.
The below approach may be useful.
I don't know your getProvider return Promise. you can set promise in getProvider method
const getProviderValue = async function(providerArray) {
return new Promise((resolve, reject) {
let importFile = '';
for (let providerId in providerArray) {
await getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
resolve(importFile)
})
}
const removeProviders = async function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = await getProviderValue(providerArray)
fs.writeFile('C:/path/to/file.txt', importFile);
})
}
Your for loop does not wait for the promises to resolve. A better way to approach this problem would be to use reduce.
providerArray.reduce(
(p, _, i) => {
p.then(_ => new Promise(resolve =>
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += entry;
resolve();
}))
);
}
, Promise.resolve() );
You can also use Promise.all but the out data may not be in the order that you expect if you append to the importFile variable.

Changing my callbacks, promise to async await

My code:
Callbacks:
const first = () => {
console.log('first');
};
const second = (callback) => {
setTimeout(() => {
console.log('second');
callback();
}, 2000);
};
const third = () => {
console.log('third');
};
first();
second(third); OUTPUT: 'first', 'second', 'third'
Promises:
const first = () => new Promise((resolve, reject) => {
resolve('first');
});
const second = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve('second');
}, 2000);
});
const third = () => {
console.log('third');
};
first()
.then((firstPromiseValue) => {
console.log(firstPromiseValue);
second()
.then((secondPromiseValue) => {
console.log(secondPromiseValue);
third();
})
}); OUTPUT: 'first', 'second', 'third'
Promise all:
const first = new Promise((resolve, reject) => {
resolve('first');
});
const second = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('second');
}, 2000);
});
const third = new Promise(function(resolve, reject) {
resolve('third');
});
Promise.all([first, second, third]).then((values) => {
console.log(values);
}); OUTPUT: ['first', 'second', 'third']
Async Await:
How to convert this above code using async await?
Which is the good flow control for javascript applications?
What about some async library which uses methods like async.waterfall, etc..
By the way, is my above code is ok or not?
How to convert this above code using async await?
async/await is not a replacement for promises, it is a replacement for then() and catch(). You'd still be using promises. So you'd take the first, second and third definition from your Promises section, and then:
async function firstSecondThird() {
let firstPromiseValue = await first();
console.log(firstPromiseValue);
let secondPromiseValue = await second();
console.log(secondPromiseValue);
third(); // not a promise, no need to await
}
firstSecondThird();
Which is the good flow control for javascript applications?
Objectively, none of them are better; but async/await is the most readable, callbacks the most explicit (with then code being in the middle).
What about some async library which uses methods like async.waterfall, etc..
Promises generally do everything those libraries do, and also got selected to be standardised. You may forget about those libraries unless you are maintaining old code that requires them.
By the way, is my above code is ok or not?
It seems to do what you want it to do, more or less, without obvious problems in efficiency or legibility. I'd say it's OK.
How to convert this above code using async await?
Using async/await is another way to use Promises. It's most nice visualy because you don't lost in then/catch but you can use it. Here you have two examples. First one with then and second one without.
const first = async (value) => {
return new Promise((resolve, reject) => {
resolve('first ');
});
};
const second = async (value) => {
return new Promise((resolve, reject) => {
resolve(value + 'second ');
});
};
const third = async (value) => {
return new Promise((resolve, reject) => {
resolve(value + 'third');
});
};
first('').then(second).then(third).then( value => console.log(value) );
const first = async () => {
return new Promise((resolve, reject) => {
resolve('first ');
});
};
const second = async () => {
return new Promise((resolve, reject) => {
resolve('second ');
});
};
const third = () => {
return 'third';
};
async function main() {
let firstValue = await first();
let secondValue = await second();
let thirdValue = third();
return firstValue + ' ' + secondValue + ' ' + thirdValue;
}
main()
.then(console.log)
.catch(err => { console.log('Error:', err) });
Which is the good flow control for javascript applications?
Normally when you need to use Promise in the middle of a function or multiple async calls fo functions.
What about some async library which uses methods like async.waterfall, etc..
I don't know about any library to use async/await. Sorry!

missing timing from promised value

So I am using Forge with View API to analyze all parts from a model which contain concrete and hide everything that is not concrete. The problem is that the properties for checking concrete are called from a DB which requires me to make it a promise. Checking for concrete is working as expected and then the problem starts. I return the Ids containing concrete, but my function which is supposed to hide it uses the Ids before the promise is resolved, so the array is empty.
console.log logs it as expected but everything else misses the timing.
My code:
getProperties(dbId) {
return new Promise((resolve, reject) => {
this.gui.getProperties(
dbId,
args => {
resolve(args.properties)
},
reject
)
})
}
async getConcreteIds() {
let wallfloorids = this.getWallIds().concat(this.getFloorIds());
let concreteIds = [];
for (let id of wallfloorids) {
let p1 = this.view.getProperties(id);
p1.then(props => {
for (let prop of props) {
if (prop.displayCategory === "Materialien und Oberflächen" && prop.displayValue.contains("Concrete")) {
concreteIds.push(id);
}
}
}).catch(() => {
});
}
return new Promise((resolve, reject) => {
try {
resolve(concreteIds)
} catch (e) {
console.log("Err", reject)
}
})
}
async onlyConcrete() {
this.getConcreteIds().then(concrete => {
debugger;
this.viewer.isolateById(concrete)
});
}
Map an array of promises for your loop and use Promise.all() to resolve after all the promises in loop resolve
Something like:
async getConcreteIds() {
let wallfloorids = this.getWallIds().concat(this.getFloorIds());
let concreteIds = [];
let promises = wallfloorids.map(id => {
let p1 = this.view.getProperties(id);
return p1.then(props => {
for (let prop of props) {
if (prop.displayCategory === "Materialien und Oberflächen" && prop.displayValue.contains("Concrete")) {
concreteIds.push(id);
}
}
});
});
return Promise.all(promises)
.then(_ => concreteIds)
.catch(err => console.log("Err", err))
}

Javascript implement async to get result of long-running process into array

I have an array of files that I am adding data to which conceptually works like this:
let filearray = ['file1.txt', 'file2.txt', 'file3.txt'];
newarray = [];
for (let f of filearray) {
newstuff = 'newstuff';
newarray.push([f, newstuff]);
}
console.log(newarray)
// Returns expected array of arrays
However, what I need to do is make newstuff = slow_promise_function(f); that involves lots of processing. How do I get the value from that promise function into the array?
Ideally I'd like to use the new async feature in ES2017.
Update:
These answers are helping me understand the problems (and solutions):
https://stackoverflow.com/a/37576787/1061836
https://stackoverflow.com/a/43422983/1061836
You could use Promise.all which returns a single Promise that resolves when all of the promises have resolved:
let loadData = async () => {
let filearray = ['file1.txt', 'file2.txt', 'file3.txt'];
try {
let ops = filearray.map(f => slow_promise_function(f));
let newarray = await Promise.all(ops);
// TODO: use newarray
} catch (err) {
console.log(err);
}
}
loadData();
async/await is a nice way to accomplish this, as you suspected:
console.log('Starting...');
let files = ['file1.txt', 'file2.txt', 'file3.txt'];
Promise.all(files.map(async f => [f, await slow_promise_function(f)]))
.then(files => console.log('got results: ', files));
function slow_promise_function(file) {
return new Promise(res => setTimeout(_ => res(`processed ${file}`), 500));
}
Well that can simply be done with Promises more info on this link Promises.
const newStuffFunc = async () => {
try {
let newStuff = await slow_promise_function(f);
// new stuff is a promise. so you either do it with await again or with then/catch
let data = await newStuff;
} catch (e){
}
}
const slow_promise_function = (url) => {
return new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
// resolve(someValue); // fulfilled
// or
// reject("failure reason"); // rejected
});
};
This link can show you more usability of async/promises into javascript.

Categories