Javascript Promise Variable Scope in For Loop - javascript

Newbie alert!
I'm trying to understand the variable scope for a promise inside a for loop.
Consider the following code:
function sleep(ms) {
return new Promise(resolve =>
setTimeout(resolve, ms)
);
}
for (var i =0; i<4; i++){
sleep(1000).then(() => console.log(i));
}
The promise is called 4 times at once and then responds with:
4
4
4
4
How can I keep reference to the i variable? In order to get
0
1
2
3
Or whatever execution order actually took place.

You can pass the i into the sleep function and resolve it.
function sleep(i, ms) {
return new Promise(resolve =>
setTimeout(() => {
return resolve(i);
}, ms);
);
}
for (var i = 0; i < 4; i++){
sleep(i, 1000).then((data) => console.log(data));
}

Your problem is just how you're calling your sleep function. You must use the await keyword inside of an async labeled function.
This should be how your code looks:
function sleep(ms) {
return new Promise(resolve =>
setTimeout(resolve, ms)
);
}
async function printSleep(){
for (var i =0; i<4; i++){
await sleep(1000).then(() => console.log(i));
}
}
printSleep();
The key part you're missing is that you must use await to actually have your code wait for the sleep function to finish. If you don't it will queue the function and continuing executing the rest of the function in a synchronous fashion, causing your then statement to execute after the for loop has finished.

As pointed out by ASDFGerte, by changing var to let, the i variable is kept within the block and outputs the desired reference.
function sleep(ms) {
return new Promise(resolve =>
setTimeout(resolve, ms)
);
}
for (let i=0; i<4; i++){
sleep(1000).then(() => console.log(i));
}

Related

How can setTimeout run in order

I have two for loop, inside them I have setTimeout function like following code:
for(let i=0;i<3;i++){
setTimeout(()=>{console.log(i)}, 1000)
}
for(let i=0;i<3;i++){
setTimeout(()=>{console.log(i)}, 1000)
}
i want the second loop does not executed until after the first loop finished,
I want this result:
0
1
2
0
1
2
- and between 2 numbers wait 1 second.
how i can do that?
I can't comment yet or I would ask clarifying questions so I'll give you what I think you're asking for. If you want a 1 second delay between each number being logged to the console then this will work:
const func = async () => {
for (let i = 0; i < 3; i++) {
await new Promise((resolve) =>
setTimeout(() => {
console.log(i);
resolve();
}, 1000)
);
}
for (let i = 0; i < 3; i++) {
await new Promise((resolve) =>
setTimeout(() => {
console.log(i);
resolve();
}, 1000)
);
}
};
func();
A quick rundown of what's happening here. I created an outer function named func which I made asynchronous so that I can use the await keyword within it. I then put the setTimeout call inside a new instance of Promise. The promise combined with the await means that javascript will essentially stop at that line until the Promise calls resolve. Once resolve is called that instance of Promise is finished and the await statement stops "blocking" javascript and the callbackque continues.
TLDR:
To use await you must be in an asynchronous function.
If await is used in front of a Promise everything will stop until the promise resolves.
Placing the resolve of the promise inside the callback given to setTimeout ensures that we will wait until each timeout finishes BEFORE the next timeout begins.
This will work
nxt (0, 0);//seed the stack
function nxt(num, itertn){
if(num == 3){//want till 2
if(itertn == 0){// first or 2nd iteration
num =0;
itertn++;
}else{
return;//after 2nd stop
}
}
console.log(num);//the work
num++;
setTimeout(nxt, 1000, num, itertn);//next after a second
}
But there are other ways to do this
I think that you're trying to do sort of a counter
Try something like this:
for(let i=1;i<=3;i++){
setTimeout(()=>{ console.log(i)}, i*1000 )
}
Let me know if this solve your problem
Try nested loop
for(let i=0;i<2;i++){
for(let j=0;j<3;j++){
setTimeout(()=>{console.log(j)}, 1000)
}
}
One solution is create promises, and with map and promise.all, you can group each loop of promises and execute them sequentially.
(async () => {
const second = (i) => new Promise(resolve => setTimeout(() => {
console.log(i)
resolve(i)
}, 1000))
const loop = () => Array(3).fill(0).map(async(item, index) => {
return await second(index)
})
var startTime = performance.now()
await Promise.all(loop())
var endTime = performance.now()
console.log(endTime - startTime)
await Promise.all(loop())
endTime = performance.now()
console.log(endTime - startTime)
})()

JS: Unsure of when to use Promise.resolve() vs returning a new Promise

I am unsure how the usage of returning a new Promise vs using a Promise.resolve() and want to make sure my understanding of these are correct.
Given these 3 functions below:
function testFunc() {
return Promise.resolve().then(() => {
//anything asynchronous in here has now become synchronous and
//execution of result of the function happens after this??
let i = 0;
while (i < 10000) {
console.log(i);
i++;
}
});
}
------
function testFunc2() {
return new Promise((resolve, reject) => {
//anything asynchronous in here is still asynchronous but the
//`resolve()` is then synchronous??
let i = 0;
while (i < 10000) {
if (i === 999) { resolve('I am a test func') };
i++;
}
})
}
------
//synchronous function
function logMe() {
let i = 0;
while (i < 10000) {
console.log("INSIDE LOG ME);
i++;
}
}
The way I understand it is that testFunc() immediately resolves the Promise and anything within there becomes synchronous. So if you were to execute:
testFunc();
logMe();
testFunc() would fully execute before logMe() was reached and executed.
For the testFunc2(), if it were executed in this order:
testFunc2();
logMe();
I understand it as the logic inside, in this case the while loop, would still execute synchronously and delay the execution of the following function, logMe(), but the resolve would be treated asynchronously.
It’s not that easy. You would have to use test Func.then(logMe()). Another option would be to run both functions in an asynchronous function:
async function run() {
await testFunc();
logMe();
}
The await is pretty simple - it runs the function and waits for it to finish, then runs the next lines. async is just there so that await can work (await does not work outside of async functions).
I prefer async/await, but many more prefer .then. It’s just what you want to use, both are very similar.
If I understand correctly what you're trying to achieve, you want to asynchronously execute operation inside the loop.
function testFunc2() {
const promises = []
for(let i = 0; i<1000; i++){
promises.push(new Promise((resolve) => {
console.log(i);
resolve()
}));
}
return Promise.all(promises);
}
function logMe() {
let i = 0;
while (i < 5) {
console.log("INSIDE LOG ME");
i++;
}
}
(async() => {
await testFunc2()
logMe();
})();

Replacing recursive function with simpler loop

I'm new to the concept of recursion. I've been practicing JavaScript with some codes from javascript30.com. I've stumbled upon the below mention function:
function peep() {
const time = randomTime(200, 1000);
const hole = randomHole(holes);
hole.classList.add('up');
setTimeout(() => {
hole.classList.remove('up');
if (!timeUp) peep();
}, time);
}
Link to full code: https://codepen.io/luckyseven444/pen/bXqXbP (code is running ok in my PC)
Is it possible to form a simple loop rather than the recursive function peep() mentioned above? I mean I want to replace the second peep() inside setTimeout function.
Thanks
If you really insist, you can create an async function, wrap setTimeout with a Promise, await the Promise and add a loop:
async function test() {
for (let i = 0; i < 10; i++) {
console.log('before setTimeout', i);
await new Promise(resolve => setTimeout(() => {
console.log('timeout elapsed', i);
resolve();
}, 1000));
}
}
test();

How can asynchronous method in loop executed in sequence?

I want to get data from db for several times. How to set a loop to execute getData() function in interval like 200ms. And if one of them success, rest of them won't be triggered. It's asynchronous method, and different from question here: Asynchronous Process inside a javascript for loop
for(var i = 0; i < 3; i++){
setTimeout(getData,200);}
This will end up with the output time interval is very close instead of 200ms, since they are asynchronous. Three "setTimeout" are triggered in a short time. like 0.001s 0.002s 0.003s, the output time is 0.201, 0.202, 2.203.
getData() returns a promise. But it can be normal function as long as it works.
You can do this by waiting for the setTimeout to finish before executing the next setTimeout
I don't believe this is possible with callbacks, as is the case with setTimeout, so you should transform it into a promise-based call
const promiseSetTimeout = timeout => new Promise(r => setTimeout(r, timeout));
const waitManyTimes = async () => {
for(let i = 0; i < 3; i++) {
await promiseSetTimeout(200);
// do something here, like getDB
console.log(i);
}
}
waitManyTimes();
Don't use a loop. Have the function call setTimeout() to run itself again if it fails.
var i = 0;
function callGetDB() {
getDB().then(db => {
// use DB
}).catch(() => {
if (i++ < 3) {
setTimeout(callGetDB, 200);
}
});
}
I'm assuming the asynchronous function getDB() returns a promise.
Using async/await:
let sleep = ms => new Promise(r => setTimeout(r, ms));
let main = async() => {
for(var i = 0; i < 3; i++) {
let db = getDB();
if (db)
return db;
await sleep(200);
}
};

Is the code in a Promise referring to the variables at the loop iteration

I've got a general question about Promises in JS.
I couldn't find a solution to my answer and finally decided to ask here.
So let's look at the following pseudo-code:
for (var i = 0; i < 10000; i++) {
some_promise(function(){
console.log(i);
// Would this i return the value of some_promise point of execution,
// or at the current iteration when it is triggered?
});
}
If we look at the first iteration and the promise is triggered, would it be 0 or let's say 1000(Iterations moving on while waiting for promise)
It would be really helpful if you could help me wrap my head around it..
In case of let the console.log will fire 10000 times and its one will have the value of i in the loop.
Example:
const fun = function(){
var promise = new Promise(resolve => {
setTimeout(resolve, 2000);
})
for(let i=0; i<1000; i++){
promise.then(() => console.log(i));
}
}
If you use var the console.log will fire 10000 printing 10000 as value:
const fun = function(){
var promise = new Promise(resolve => {
setTimeout(resolve, 2000);
})
for(var i=0; i<1000; i++){
promise.then(() => console.log(i));
}
}
That's because var is scoped to the nearest function block and let is scoped to the nearest enclosing block, your loop is synchronous and will fire at least on "tick" before the promise is resolved.

Categories

Resources