How to wait a Promise inside a forEach loop - javascript

I'm using some Promises to fetch some data and I got stuck with this problem on a project.
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
listExample.forEach((item,index) => {
console.log(item);
example1().then(() => {
console.log("First");
s = item;
});
example2().then(() => {
console.log("Second");
});
});
console.log("The End");
};
If I call the doStuff function on my code the result is not correct, the result I expected is shown below.
RESULT EXPECTED
a a
b First
c Second
The End b
First First
Second Second
First c
Second First
First Second
Second The End
At the end of the function no matter how I try, the variable s gets returned as "", I expected s to be "c".

It sounds like you want to wait for each Promise to resolve before initializing the next: you can do this by awaiting each of the Promises inside an async function (and you'll have to use a standard for loop to asynchronously iterate with await):
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = async () => {
const listExample = ['a','b','c'];
for (let i = 0; i < listExample.length; i++) {
console.log(listExample[i]);
await example1();
const s = listExample[i];
console.log("Fisrt");
await example2();
console.log("Second");
}
console.log("The End");
};
doStuff();
await is only syntax sugar for Promises - it's possible (just a lot harder to read at a glance) to re-write this without async/await:
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = () => {
const listExample = ['a','b','c'];
return listExample.reduce((lastPromise, item) => (
lastPromise
.then(() => console.log(item))
.then(example1)
.then(() => console.log("Fisrt"))
.then(example2)
.then(() => console.log('Second'))
), Promise.resolve())
.then(() => console.log("The End"));
};
doStuff();

If you want NOT to wait for each promise to finish before starting the next;
You can use Promise.all() to run something after all your promises have resolved;
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
let promises = []; // hold all the promises
listExample.forEach((item,index) => {
s = item; //moved
promises.push(example1() //add each promise to the array
.then(() => {
console.log(item); //moved
console.log("First");
}));
promises.push(example2() //add each promise to the array
.then(() => {
console.log("Second");
}));
});
Promise.all(promises) //wait for all the promises to finish (returns a promise)
.then(() => console.log("The End"));
return s;
};
doStuff();

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 function is not waiting at await

I need to scroll down before doing some action. my promise function is not waiting in await line. how can I solve this problem?
let scrollingPromise = new Promise(async (resolve, reject) => {
// I need this part to be done before resolving
await page.evaluate(() => {
const scrollingWindow = document.querySelector('.section-layout.section-scrollbox.scrollable-y.scrollable-show');
var scrollCount = 0;
function scrollFunction(){
setTimeout(()=>{
scrollingWindow.scrollBy(0, 5000);
scrollCount++;
if(scrollCount < 5){
scrollFunction();
}
}, 3000);
};
scrollFunction();
});
// but it resolves instantly doesn't wait for previous await
resolve();
});
//this .then() runs before my scrolling
scrollingPromise.then(async () => {
// ...
}
You're mixing await and Promise syntaxes. You're awaiting an asynchronous function that doesn't return a Promise, so it can't work. I believe you're trying to do this :
let scrollingPromise = new Promise((resolve, reject) => {
page.evaluate(() => {
const scrollingWindow = document.querySelector('.section-layout.section-scrollbox.scrollable-y.scrollable-show');
var scrollCount = 0;
function scrollFunction() {
setTimeout(() => {
scrollingWindow.scrollBy(0, 5000);
scrollCount++;
if (scrollCount < 5) {
scrollFunction();
} else {
resolve() // <-- Done, resolve here
}
}, 3000);
};
scrollFunction();
});
});

Create a Promise that resolves when variable is not undefined

I'm trying to Create a Promise that resolves when variable is not undefined.
Code example
https://codesandbox.io/s/quizzical-feynman-ktvox?file=/src/index.js
let fetchedPromise = new Promise((resolve, reject) => {
const check = ()=>{
setTimeout(() =>{
console.log("checking")
if (dataFetched) resolve(dataFetched);
else check()
}, 100);
}
check()
});
const waitForDataFromAnotherComponent = async () => {
let result = await fetchedPromise;
console.log("Result: ", result);
};
const assignData = () => {
setTimeout(() => {
dataFetched = 1000;
console.log(dataFetched);
}, 5000)
};
waitForDataFromAnotherComponent();
assignData();
This works but I find it inefficient as it's callstack prone and seems wrong.
Other non-working solutions I've tried:
//-------------SOLUTION 1
let fetchedPromise = new Promise((resolve, reject) => {
const check = ()=>{
if (dataFetched) resolve(dataFetched);
else check()
}
check()
});
//--------------------SOLUTION 2
let fetchedPromise = new Promise((resolve, reject) => {
if (dataFetched) resolve(dataFetched);
});
Scenario
I need a function like solution 3 that doesn't rely on setTimeout
Solved by using Javascript Proxy
Basically I assign a Proxy Object to dataFetched that listens to changes.
I re-create the function of listening due to the fact that it must include resolve()
let dataFetched
let x = {
aListener: function (val) {},
set a(val) {
dataFetched = val;
this.aListener(val);
},
get a() {
return dataFetched;
},
registerListener: function (listener) {
this.aListener = listener;
}
};
let fetchedPromise = new Promise((resolve, reject) => {
x.registerListener(function (val) {
console.log("yeyyyyyyyyyyyyyyy");
if (dataFetched) resolve(dataFetched);
});
});
const waitForDataFromAnotherComponent = async () => {
let result = await fetchedPromise;
console.log("Result: ", result);
};
const assignData = async () => {
await new Promise((resolve, reject) =>
setTimeout(() => {
x.a = 1000;
console.log(dataFetched);
resolve(dataFetched);
}, 1000)
);
};
waitForDataFromAnotherComponent();
assignData();
EDIT
Actually it's possible to externalize the resolve() function of the promise but with some downsides as stated here
example
let dataFetched
var promiseResolve, promiseReject;
let x = {
aListener: function (val) {
if (dataFetched) promiseResolve(dataFetched);
},
set a(val) {
dataFetched = val;
this.aListener(val);
},
get a() {
return dataFetched;
},
registerListener: function (listener) {
this.aListener = listener;
}
};
let fetchedPromise = new Promise((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});

Putting up delay in promises

So, I have two promises that I want to print on screen with a 3sec delay in between. How would I achieve it. Below is the code.
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
Promise.all([promiseOne, promiseTwo]).then(function() {
setTimeout(() => {
const response = Promise.all.next();
console.log(response);
}, 3000);
});
I suggest you use a delay function. First you loop through each response in the array and use the delay in between
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Promise.all([promiseOne, promiseTwo]).then(async function(resps) {
for(let res of resps){
console.log(res);
await delay(3000)
}
});
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
Promise.all([promiseOne, promiseTwo]).then(function(all) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(all);
}
, 3000);
});
}).then(console.log)
Return new Promise in then, that promise waits for x millisecond and resolve it.
And do anything in next then
If you just want to delay the log then you can add response parameter as explained by #Daniel Rodríguez Meza.
But if you want to delay response from any one of the promise promiseOne or promiseTwo then you should use setTimeout(() => resolve("Hello"), 300); inside respective promise as shown below. Also do not use setTimeout inside Promise.All.
As per OP's comment I have updated answer to resolve promiseTwo 3 seconds after promiseOne resolves.
Here I've assigned resolve of promiseTwo to global variable resolvePromiseTwo which is used inside promiseOne to resolve promiseTwo after 3 seconds.
Note I have used .then after promiseOne and promiseTwo just to verify output. You can omit both.
let resolvePromiseTwo = null;
const promiseOne = new Promise(function(resolve, reject) {
resolve("Good Morning")
setTimeout(() => resolvePromiseTwo("Hello"), 3000);
}).then(res => {
console.log('promiseOne resolved');
return res;
});
const promiseTwo = new Promise(function(resolve, reject) {
resolvePromiseTwo = resolve;
}).then(res => {
console.log('promiseTwo resolved');
return res;
});
Promise.all([promiseOne, promiseTwo]).then(function(response) {
console.log('Promise.all');
console.log(response);
});
You were pretty close, you just need to receive the value from the results of the Promise.all and then handle that information, like this:
const promiseOne = new Promise(function(resolve) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve) {
resolve("Good Morning");
});
Promise.all([promiseOne, promiseTwo])
.then(function(response) {
setTimeout(() => {
console.log(response);
}, 3000);
});
Edit
According to the clarification given by OP what he needs is the following:
After the first promise is resolved, wait 3 seconds and then execute the second promise.
const promiseOne = new Promise(function(resolve) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve) {
resolve("Good Morning");
});
async function resolveWithDelay(delay = 3000) {
const res1 = await promiseOne;
console.log(res1);
setTimeout(async () => {
const res2 = await promiseTwo;
console.log(res2);
}, delay);
}
resolveWithDelay();

Why does async array map return promises, instead of values

See the code below
var arr = await [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
console.log(arr); // <-- [Promise, Promise, Promise ....]
// i would expect it to return [1,2,3,4,5]
Quick edit:
The accepted answer is correct, by saying that map doesnt do anything special to async functions. I dont know why i assumed it recognizes async fn and knows to await the response.
I was expecting something like this, perhaps.
Array.prototype.mapAsync = async function(callback) {
arr = [];
for (var i = 0; i < this.length; i++)
arr.push(await callback(this[i], i, this));
return arr;
};
var arr = await [1,2,3,4,5].mapAsync(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
// outputs 1, 2 ,3 ... with 1 second intervals,
// arr is [1,2,3,4,5] after 5 seconds.
Because an async function always returns a promise; and map has no concept of asynchronicity, and no special handling for promises.
But you can readily wait for the result with Promise.all:
try {
const results = await Promise.all(arr);
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
(async() => {
try {
console.log(await Promise.all(arr));
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
})();
.as-console-wrapper {
max-height: 100% !important;
}
or using Promise syntax
Promise.all(arr)
.then(results => {
// Use `results`, which will be an array
})
.catch(err => {
// Handle error
});
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Promise.all(arr)
.then(results => {
console.log(results);
})
.catch(err => {
// Handle error
});
.as-console-wrapper {
max-height: 100% !important;
}
Side note: Since async functions always return promises, and the only thing you're awaiting in your function is a promise you create, it doesn't make sense to use an async function here anyway. Just return the promise you're creating:
var arr = [1,2,3,4,5].map((index) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Of course, if you're really doing something more interesting in there, with awaits on various things (rather than just on new Promise(...)), that's different. :-)
Since it is async, the values have not been determined at the time map returns. They won't exist until the arrow function has been run.
This is why Promises exist. They are a promise of a value being available in the future.

Categories