Output results of two promises into a single object - javascript

I have two asynchronous functions which create promises. Both functions use the same input i.
async function1(i) {}
async function2(i) {}
I will be calling these functions multiple times with different values for my input, and I want to make my code as efficient as possible, so I want to queue the promises and have them run in parallel using Promise.all().
However, I want to get a single result as my final output, which will be an array of objects like so:
[
{
input: i,
result1: result1,
result2: result2
},
...
]
I have accomplished this in two descrete steps:
async function function1(i) {
return i * i
}
async function function2(i) {
return i * i * i
}
async function main() {
var promises = []
for (let i = 0; i < 10; i++) {
let promise = function1(i)
.then(function(result1) {
return {i:i, result1:result1}
});
promises.push(promise)
}
var final = await Promise.all(promises)
var promises2 = [];
for (let i = 0; i < 10; i++) {
let promise = function2(i)
.then (function(result2) {
final[i]['result2'] = result2;
});
promises2.push(promise);
}
await Promise.all(promises2)
console.log(final)
}
main()
However, I feel like this can be accomplished using a single Promise.all(). Can you tell me how?

async function function1(i)
{
return i * i
}
async function function2(i)
{
return i * i * i
}
async function main()
{
const promises = [];
for (let i = 0; i < 10; i++)
promises.push(function1(i), function2(i));
const results = await Promise.all(promises);
const data = [];
for (let i = 0; i < 10; i++)
{
const [result1, result2] = results.slice(i * 2, i * 2 + 2);
data.push({ i, result1, result2 });
}
console.log(data);
}
main();
This should work smoothly and quickly. The functions function1 and function2 return Promises if you do not await them, so pushing them onto the promises array is self-explanatory.
Then you await Promise.all, which waits until all 20 promises that were fired off have completed. Finally, the second loop runs through the promises that were returned.
In my code I used destructuring assignments. I assume those are available to you since you are using async functions, which implies that you can use ES2017.

EDIT: 333's answer is correct, not mine. This solution will have 2 batches in the queue.
I think I figured it out:
console.log("start")
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function function1(i) {
await sleep(2000);
return i * i
}
async function function2(i) {
await sleep(2000);
return i * i * i
}
async function main() {
var promises = []
for (let i = 0; i < 10; i++) {
let a = function1(i);
let b = function2(i);
let promise = Promise.all([a,b]).then(
function([resulta,resultb]) {
return {i:i, result1: resulta, result2: resultb}
});
promises.push(promise)
}
var final = await Promise.all(promises)
console.log(final)
}
main()
Note that I added a sleep function to test the parallelization of these functions. Everything runs in 2 seconds, so I think it is optimized.

Related

Generator next parameter in `for await`

When you have an async generator, you can call next(x) to return a result to the generator.
async function* generate() {
for (let i = start; i <= 5; i++) {
const result = yield i;
console.log("result", result)
}
}
;(async () => {
const generator = generate()
await generator.next(1)
await generator.next(2)
})();
This will print 1 and 2 as this is the next parameter that is passed as a result back to the generator.
How to replicate this same behaviour but using await for ?
for await (let value of generate()) {
// ... no way to send `next` parameter here
}

JavaScript getting array index from promise

The script below returns promise, there is an array inside it with two index, i want to get index=0 and index=1 separately and output them, how can i do it without using console.log?
async function a1(callback) {
var a = 2 + 2;
return await [a, callback()];
}
async function a2() {
var b = 2 + 3;
return await b;
}
console.log(a1(a2));
My question for Artash Grigoryan
In javascript, async functions always return promise.
I am not entirely sure about your intentions here, but looking at your drawing I would assume that you need to add an extra await on lines 3 and 9.
This code should work for you:
async function a1(callback) {
var a = 2 + 2;
return await [a, await callback()];
}
async function a2() {
var b = 2 + 3;
return await b;
}
await a1(a2);
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
I don't really understand why without console.log, but You can do this:
async function main() {
console.error(await a1(a2));
}
async function a1(callback) {
var a = 2 + 2;
return await [a, callback()];
}
async function a2() {
var b = 2 + 3;
return await b;
}
main();
a2 is an async function , so callback() wont be resolved here in this statement return await [a,callback()].
You can check if the callback is of type AsyncFunction , if so then you can wait for it to get resolved before returning the value
async function a1(callback) {
var a = 2 + 2;
if (callback.constructor.name === 'AsyncFunction') {
const m = await callback().then(val => val)
return [a, m]
}
return await [a, callback];
}
async function a2() {
var b = 2 + 3;
return await b;
}
a1(a2).then(d => console.log(d))

Async/Await is not working as expected with for loop statement

I am new to Javascript and nodejs. While trying to understand how promises and callbacks work, i tried calling a function in a 'for' loop as shown below. What i was trying to achieve is to print 'i' value every 2 seconds using promises. However, the program waits for 2 seconds and prints i value 3 times and exits.
for(let i = 0; i < 3 ; i++){
func(callback,i);
}
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Can anybody please help me understand why this is happening?
You are pretty close. The missing information is async functions (your func()) implicitly returns an AsyncFunction object which implicitly returns a Promise itself too (doc).
Your code using immediately invoked function expression would be
(async () => {
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
})()
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Please note now in most of the browsers is natively available the construct for await..of (doc). You can try experimenting this one too.
You can just wrap your loop with async immediately executed function and add await within it (as was already suggested here):
(async () => {
for(let i = 0; i < 3 ; i++){
await callback(i);
}
})();
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
Here is my original answer before Bergi's edit:
(async () => {
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
})()
async function func(callback,i){
await callback(i);
}
function callback(i){
return new Promise((res,rej) =>{
setTimeout(()=>{
console.log('i = ',i)
res();
}, 2000);
})
}
You need to wait for async function to complete
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
But since you can't use await keyword in the global scope, you will need to wrap your for loop in an async function, and than call it
async function myTest(){
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}
}
myTest()
Here are some functions which help you understand how promises work with Arrays, where we make common mistakes.
function promiseFunction(v) {
return new Promise((resolve) => {
setTimeout(() => resolve(v), 1000);
});
}
function f1() {
[1, 2, 3, 4]. forEach(async(i) => {
const v = await promiseFunction(i);
console.log(`-f1 v- ${v}<br/>`);
});
console.log('all done<br/>');
}
async function f2() {
await Promise.all([1, 2, 3, 4].map(async(i) => {
const v = await promiseFunction(i);
console.log(`-f2 v- ${v}<br/>`);
}));
console.log('all done<br/>');
}
async function f3() {
await [1, 2, 3, 4].reduce((p, i) => {
return p.then(async () => {
const v = await promiseFunction(i);
console.log(`-f3 v- ${v}<br/>`);
});
}, Promise.resolve());
console.log('all done<br/>');
}
async function func() {
console.log('f1');
await f1();
console.log('f2');
await f2();
console.log('f3');
await f3();
}
func();
f1() will print all done first and then print 1,2,3,4 at once after a second.
f2() will print 1,2,3,4 at once after a second then print all done.
f3() will print 1,2,3,4 in every second, and then print all done.
You async function func also returns a promise so you need the await keyword before its call.
for(let i = 0; i < 3 ; i++){
await func(callback,i);
}

Axios.get().then() in a for loop

How would I go about running Axios in a for loop, each with a corresponding .then() function. Then after the for loop ends, run another function.
Example:
const array = ['asdf', 'foo', 'bar'];
let users = [];
for (i = 0; i < array.length; i++) {
axios.get('/user/' + array[i].id).then(response => {
// do something with response
users.push(response);
});
}
console.log(users);
const array = [{ id: 'asdf'}, { id: 'foo' }, { id: 'bar' }]; // changed the input array a bit so that the `array[i].id` would actually work - obviously the asker's true array is more than some contrived strings
let users = [];
let promises = [];
for (i = 0; i < array.length; i++) {
promises.push(
axios.get('/user/' + array[i].id).then(response => {
// do something with response
users.push(response);
})
)
}
Promise.all(promises).then(() => console.log(users));
The .then() method of a Promise itself returns a Promise; so you can collect those and await all of them with Promise.all().
Note that even if you're doing this within an async function, you don't want to await inside the for-loop, because then each request will wait for the previous one to finish before it even starts, and presumably you want to run these requests in parallel.
Depending on your use case, a concise async / await function might look like this:
async function getMultiple(...objectsToGet) {
let users = [];
await Promise.all(objectsToGet.map(obj =>
axios.get('/user/' + obj.id).then(response => {
// do something with response
users.push(response);
})
));
return users;
}
// some other async context
console.log(await getMultiple({ id: 'asdf'}, { id: 'foo' }, { id: 'bar' }));
If you are using a more recent version of javascript with async/await support, you can do the following:
const array = ['asdf', 'foo', 'bar'];
let users = [];
for (const id in array) {
const response = await axios('/user/' + id);
users.push(response);
}
console.log(users);
You should collect all the promises inside an array and use promise.all in the following manner -
const array = ['asdf', 'foo', 'bar'];
let promises = [];
for (i = 0; i < array.length; i++) {
promises.push(axios.get('/user/' + array[i].id))
}
Promise.all(promises)
.then(responses => console.log(responses));

Resolving Promise.all with a promise as part of array or object

I am trying to queue a bunch of asynchronous calls to fire in parallel. However, the promises I am queuing have additional data that I want to keep along with the promise value.
My question is: how can I pass an object or array, which contains a promise, and have the promises resolve to a value within the object?
For example, let’s generate a normal array of promises:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asyncFunction(i));
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
This is working just fine. But now if we try to place the promise into an object, with some metadata:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
The value in value is a promise, and not the resolved value.
My expected output is:
[{"id":0,"value":0},{"id":1,"value":10},{"id":2,"value":20},...]
You can push promises that resolve to the format you want:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(
asyncFunction(i).then(value => ({
id: i,
value,
}))
);
}
let result = await Promise.all(promises);
console.log(result);
}
main();
You could map over the array of promises and await the value key containing the promise to resolve
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises.map(async (promise) => ({
id: promise.id,
value: await promise.value
})));
document.getElementById('output').innerText = JSON.stringify(result);
}
main();

Categories

Resources