NodeJS - Wait for return inside setTimeout - javascript

I am trying to learn async/await. I want to wait for the return statement inside my async function. I have to call it several times so I used a setTiemout inside.
EDIT:
//Processing gallery
async function somefunction(){
async function getPictureR(){
/* some code */
if($('.actions > .prev', html)[0]){
older = $('.actions > .prev', html)[0].attribs.href;
} else {
console.log('return');
return;
}
/* some code */
return new Promise((resolve, reject) => {
setTimeout(getPictureR, 1 * 1000/2);
})
}
await getPictureR();
console.log('getPictureR done');
}
I've tried await getPictureR() but it triggers right after the first call to the function. How can I wait for that return ?

You should never call a promise-returning function, like getPictureR, from an asynchronous (non-promise) callback or inside the new Promise constructor. You also were never resolving the new Promise. You are looking for
return new Promise((resolve, reject) => {
setTimeout(resolve, 1 * 1000/2);
}).then(() => {
return getPictureR(); // do the promise call in a `then` callback to properly chain it
})
But since you're using async/await, you don't need the recursive function and the then chaining anyway. Also you can factor out the setTimeout-in-promise wrapping in a separate helper function:
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function somefunction() {
while (true)
/* some code */
const prev = $('.actions > .prev', html);
if (prev.length) {
older = prev[0].attribs.href;
} else {
console.log('return');
break;
}
/* some code */
await delay(1 * 1000/2);
// ^^^^^^^^^^^
}
console.log('getPicture done');
}

Related

How to asynchronously call a function with setTimeout twice

I'm trying to create a typewriter effect with setTimeout().
My code for the typewriter function is as follows:
function typeWriter(toWrite,isDelete=false){
if(!isDelete && i<toWrite.length){
document.getElementById("typewrite").innerHTML+=toWrite.charAt(i);
i++;
speed=Math.random()*100+100;
setTimeout(typeWriter,speed,toWrite,false);
}
else if(isDelete && i<toWrite.length){
var typewrite=document.getElementById("typewrite");
typewrite.innerHTML=typewrite.innerHTML.slice(0,-1);
i++;
speed=100;
setTimeout(typeWriter,speed,toWrite,true);
}
}
And I want to call the code twice, once to write a string, and then a second time to delete a part of it.
My grasp on promises is still very shaky, and my attempt (below) didn't really change anything:
const intro=new Promise((resolve,reject)=>{
resolve();
})
intro
.then(typeWriter("hello world"))
.then(typeWriter("world",true))
When I run the code, instead of having "Hello world" get typed and then delete the "world". Both functions start going synchronously and the final output is "world".
I've been banging my head on this for longer than I'm comfortable admitting, I would appreciate any help.
to be chainable, typeWriter has to return a function.
e.g.
function typeWriter(toWrite,isDelete=false) {
return new Promise((resolve, reject) => {
})
}
If you were to use a setTimout, and you want to wait for the function to be called before going to the next of the chain, place the resolve in the settimeout.
e.g.
function typeWriter(toWrite,isDelete=false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hello i am done")
}, 1000)
})
}
Note, I'm using the same syntax as you - e.g
.then(typeWriter("hello world"))
But in this case, typeWriter returns a function, so it's legit
const typeWriter = (toWrite, isDelete) => {
const typewrite=document.getElementById("typewrite");
if (!isDelete) {
const fn = async (position = 0) => {
if (position < toWrite.length) {
typewrite.innerHTML += toWrite.charAt(position);
await new Promise(resolve => setTimeout(resolve, Math.random()*100 + 100));
return fn(position+1);
}
};
return () => fn(0);
} else {
const fn = async (position) => {
if (position < toWrite.length) {
typewrite.innerHTML = typewrite.innerHTML.slice(0,-1);
await new Promise(resolve => setTimeout(resolve, 100));
return fn(position + 1);
}
};
return () => fn(0);
}
};
Promise.resolve()
.then(typeWriter("hello world"))
.then(typeWriter("world",true))
<div id="typewrite"></div>
You're calling typeWriter immediately, while you really intended to pass that call to the then method, and have that only called when the relevant promise resolves. You can do that by passing () => typeWriter(......) as anonymous function to then.
Secondly, typeWriter will then have to return a promise, so the next chained then callback will only be called when that promise resolves.
I would also change the signature of your function a bit, so that it first deletes a given number of characters (can be 0) and then inserts the given string.
Here is how that looks:
// Promisify setTimeout:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// Make async, so it returns a promise:
const typeWriter = async (numDelete, toWrite) => {
const typewrite = document.getElementById("typewrite");
// First delete a given number of characters
while (numDelete-- > 0) {
typewrite.textContent = typewrite.textContent.slice(0, -1);
await delay(100);
}
// Then append new characters
for (const letter of toWrite) {
typewrite.textContent += letter;
await delay(Math.random()*100 + 100);
}
};
Promise.resolve()
// Pass a callback -- don't call typeWriter yourself
.then(() => typeWriter(0, "hello world"))
.then(() => typeWriter(5, ""));
<div id="typewrite"></div>

JavaScript Promises: Executing Promises Sequentially

In an attempt to understand promises more clearly, i have been reading up a few very interesting articles on the same. I came across the following code which works perfectly for executing promises sequentially. But i am not able to understand how it works.
function doFirstThing(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(1);
},1000)
})
}
function doSecondThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(res + 1);
},1000)
})
}
function doThirdThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(res + 2);
},1000)
})
}
promiseFactories = [doFirstThing, doSecondThing, doThirdThing];
function executeSequentially(promiseFactories) {
var result = Promise.resolve(); // this is the most problematic line
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);// what is happening here ?
});
return result;
}
executeSequentially(promiseFactories)
I do understand that promises are executed as soon as they are created. For some reason i am not able to understand the flow of execution. Especially this following line:
var result = Promise.resolve()//and empty promise is created.
Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?
result = result.then(promiseFactory);
I tried replacing the 'forEach' with a 'map' function and still yielded the same result. i.e, the methods where executed sequentially.
Also, how is the value passed from one chained function to other ?
Any help or article/blog is highly appreciated.
You can image a Promise as a box with execution inside. As far as the promise is created, the execution starts. To get the result value, you have to open the box. You can use then for it:
Promise.resolve(5).then(result => console.log(result)); // prints 5
If you want to chain promises you can do it by opening the box one by one:
Promise.resolve(5)
.then(result => Promise.resolve(result + 1))
.then(result => Promise.resolve(result * 2))
.then(result => console.log(result)); // prints 12
This chaining makes the executions synchronous (one by one).
If you want to execute several promises asynchronously (you don't chain results), you can use Promise.all:
Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
.then(result => console.log(result)); // prints [1,2,3]
In your case:
Promise.all(promiseFactories).then(result => console.log(result));
Another option how to work with promises is to await them:
(async ()=> {
var res1 = await Promise.resolve(5);
var res2 = await Promise.resolve(res1 + 1);
var res3 = await Promise.resolve(res2 * 2);
console.log(res3); // prints 12
})();
await works similar to then - it makes asynchronous execution to synchronous.
In your case:
async function executeSequentially(promiseFactories) {
for (const p of promiseFactories) {
const result = await p;
console.log(result);
}
}
Note: await packs a value into a Promise out of the box:
var res1 = await 5; // same as await Promise.resolve(5)
The executeSequentially method returns all the Promises one after each other. It happens to iterate over promiseFactory, but it could be written as:
function executeSequentially(promiseFactories) {
return doFirstThing()
.then(() => doSecondThing())
.then(doThirdThing() );
}
It is just the same. We are basically returning a Promise.
Now, however, we want to iterate over a collection of promises.
When iterating, we need to attach the current Promise to the previous with a then. But the forEach does not expose the next Promise -or the previous- in every iteration. And yet we still need it in order to keep chaining Promises one by one. Hence, the result 'hack':
function executeSequentially(promiseFactories) {
var result = Promise.resolve(); /*We need a thing that keeps yelling
the previous promise in every iteration, so we can keep chaining.
This 'result' var is that thing. This is keeping a Promise in every
iteration that resolves when all the previous promises resolve
sequentially. Since we don't have a Promise in the array
previous to the first one, we fabricate one out of 'thin air'
with Promise.resolve() */
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory); /* Here result is update
with a new Promise, with is the result of chaining `result`
with the current one. Since `result` already had all the previous ones,
at the end, `result` will be a Promise that depends upon all the
Promises resolution.*/
});
return result;
}
Now, there's also a syntax quirk that maybe is puzzling you:
result = result.then(promiseFactory);
This line is pretty much the same as the following:
result = result.then(resolvedValue => promiseFactory(resolvedValue));
Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?
First thing first, promiseFactory is a pretty bad name there. The method should be better written as follows:
function executeSequentially(promises) {
var result = Promise.resolve(); // this is the most problematic line
promises.forEach(function (currentPromise) {
result = result.then(currentPromise);// what is happening here ?
});
return result;
}
So:
how calling the currentPromise method inside the 'then' method of the empty promise makes it execute sequentially?
It makes execute sequentially because when you attach a Promise to another by then, it executes sequentially. Is a then thing, it is not at all related to the fact that we are iterating over Promises. With plain Promises outside an iteration it works pretty much the same:
Promise.resolve() // fake Promises that resolves instanly
.then(fetchUsersFromDatabase) // a function that returns a Promise and takes
// like 1 second. It won't be called until the first one resolves
.then(processUsersData) // another function that takes input from the first, and
// do a lot of complex and asynchronous computations with data from the previous promise.
// it won't be called until `fetchUsersFromDatabase()` resolves, that's what
// `then()` does.
.then(sendDataToClient); // another function that will never be called until
// `processUsersData()` resolves
It is always recommended to use Promise.all if you want such behaviour:
function doFirstThing() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(1);
}, 1000)
})
}
function doSecondThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res + 1);
}, 1000)
})
}
function doThirdThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res + 2);
}, 1000)
})
}
let promiseFactories = [doFirstThing(2), doSecondThing(1), doThirdThing(3)];
Promise.all(promiseFactories)
.then(data => {
console.log("completed all promises", data);
})
To run it sequentially one after another:
function doFirstThing() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(1);
}, 1000)
})
}
function doSecondThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res + 1);
}, 3000)
})
}
function doThirdThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res + 2);
}, 5000)
})
}
promiseFactories = [doFirstThing, doSecondThing, doThirdThing];
function executeSequentially(promiseFactories) {
promiseFactories.forEach(function(promiseFactory) {
promiseFactory(1).then((data) => {
console.log(data)
});
});
}
executeSequentially(promiseFactories);
If we lay out the foreach loop it will look like the following
function doFirstThing(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(1);
resolve(1);
},1000)
})
}
function doSecondThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(2);
resolve(res + 1);
},2000)
})
}
function doThirdThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(3);
resolve(res + 2);
},3000)
})
}
Promise.resolve()
.then(doFirstThing())
.then(doSecondThing())
.then(doThirdThing());
I do understand that promises are executed as soon as they are created. For some reason i am not able to understand the flow of execution. Especially this following line:
var result = Promise.resolve()//and empty promise is created.
This is just to get hold of the promise chain's starting point. Here it is an already resolved promise. To better understand it you can use one of your promises to get hold of the promise chain like below.
let promiseFactories= [doSecondThing, doThirdThing];
let result = doFirstThing();
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);
});
This will also work.

Wait for nested JS promise to finish before resolving original promise

I am new to Promises and I'm having a little trouble with the concept of waiting for a nested promise to finish all executions before resolving the original promise.
Original Code
function getSomething(text) {
return new Promise(function (resolve, reject) {
getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}.then(function (array) {
var promise = new Promise(function (resolve, reject) {
/* anotherFunction() is asynchronous */
result = anotherFunction(array); <-- Spot 1
});
promise.then(function () { });
});
return resolve(result); <--- Spot 2
}
else {
return resolve(null);
}
});
});
};
Updated Code - better but still not fully working like I want.
function getSomething(text) {
return getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
return getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}).then(anotherFunction);
} else {
return null;
}
});
}
Then, inside of the individual View page, I have this:
getSomething(text).then(function (result) {
/* Do something here using result */
/* But result is undefined and I think still pending */
});
I have optimized the original function using Thomas's help but the original call inside the view still seems to be continuing before result is formulated.
I want getSomething() to full complete and return result before I execute the code inside of the .then that is inside the view. So far, that is not being accomplished.
I have found a couple posts that have pointed me in what I think is the correct direction of Promise.all but I can't really seem to get anywhere with that information so I was hoping someone could help explain it for my specific situation.
The posts that have helped are:
Promise Chain not waiting for promises to resolve before ending
Wait until nested promises resolve
Wait for promises inside Promise.all to finish before resolving it
Solution
The original issue was solved by Thomas in the marked answer. The final problem was solved inside of anotherFunction() upon further inspection.
Originally:
function anotherFunction(array) {
return new Promise(function (resolve, reject) {
return anotherGet(parcels).then(function (list) {
/* Do stuff here without a return */
});
});
}
Fixed Version:
function anotherFunction(array) {
return anotherGet(parcels).then(function (list) {
/* Do stuff here */
return list;
});
}
First, avoid the Promise/Deferred antipattern. Rarely you need to create your own promises, usually you have some function that returns a Promise; use that.
Second, for the outer Promise to wait for a nesed PRomise, you need to return that nested Promise to the outer Promise.
Returning a Promise inside of then() will make the resulting Promise to resolve to the value of the Promise you retuned inside.
And last, something like .then(value => value) or .then(value => Promise.resolve(someFunction(value)))is pointless. Your wrapper around anotherFunction basically just passed through the argument and returned the result.
So, your Pseudocode should look something like this:
function getSomething(text) {
return getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
return getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}).then(anotherFunction);
} else {
return null;
}
});
}
The basic trick is to make sure all of your resolve() calls are inside of the nested promises' then() methods.
Something along the lines of:
function getSomething(text) {
return new Promise(function (resolve, reject) {
getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}.then(function (array) {
var promise = new Promise(function (resolve, reject) {
result = anotherFunction(array); <-- Spot 1
});
promise.then(function () { });
resolve(result); <--- Spot 2
});
}
else {
resolve(null);
}
});
});
};
If it needs to be nested in the promise.then() above, just stick it in there instead.
Also note that you don't need to return your resolves. Just call them.

ES6 promises with timeout interval

I'm trying to convert some of my code to promises, but I can't figure out how to chain a new promise inside a promise.
My promise function should check the content of an array every second or so, and if there is any item inside it should resolve. Otherwise it should wait 1s and check again and so on.
function get(){
return new Promise((resolve) => {
if(c.length > 0){
resolve(c.shift());
}else{
setTimeout(get.bind(this), 1000);
}
});
}
let c = [];
setTimeout(function(){
c.push('test');
}, 2000);
This is how I expect my get() promise function to work, it should print "test" after 2 or 3 seconds max:
get().then((value) => {
console.log(value);
});
Obviously it doesn't work, nothing is ever printed
setTimeout has terrible chaining and error-handling characteristics on its own, so always wrap it:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
function get(c) {
if (c.length) {
return Promise.resolve(c.shift());
}
return wait(1000).then(() => get(c)); // try again
}
let c = [];
get(c).then(val => console.log(val));
wait(2000).then(() => c.push('test'));
While you didn't ask, for the benefit of others, this is a great case where async/await shines:
const wait = ms => new Promise(r => setTimeout(r, ms));
async function get(c) {
while (!c.length) {
await wait(1000);
}
return c.shift();
}
let c = [];
get(c).then(val => console.log(val));
wait(2000).then(() => c.push('test'));
Note how we didn't need Promise.resolve() this time, since async functions do this implicitly.
The problem is that your recursive call doesn't pass the resolve function along, so the else branch can never call resolve.
One way to fix this would be to create a closure inside the promise's callback so that the recursive call will have access to the same resolve variable as the initial call to get.
function get() {
return new Promise((resolve) => {
function loop() {
if (c.length > 0) {
resolve(c.shift());
} else {
setTimeout(loop, 1000);
}
}
loop();
});
}
let c = [];
setTimeout(function() {
c.push('test');
}, 2000);
get().then(val => console.log(val));
In the else case, you never resolve that promise. get might create another one, but it is returned to nowhere.
You should promisify your asynchronous function (setTimeout) on the lowest level, and then only chain your promises. By returning the result of the recursive call from a then callback, the resulting promise will resolve with the same result:
function delayAsync(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
function get(c) {
if (c.length > 0){
return Promise.resolve(c.shift());
} else {
return delay(1000).then(() => {
return get(c); // try again
});
}
}
What you need is a polling service, which checks periodically for specific condition prior proceeding with promise resolution. Currently when you run setTimeout(get.bind(this), 1000); you are creating a new instance of the promise without actually resolving the initial promise, because you don't reference to the initial resolve function that you created.
Solution:
Create a new callback function that you can reference to it inside the promise
Pass the resolve & reject as params in the setTimeout invocation e.g. setTimeout(HandlePromise, 1000, resolve, reject, param3, param4 ..); setTimeout API
function get() {
var handlerFunction = resolve => {
if (c.length > 0) {
resolve(c.shift());
} else {
setTimeout(handlerFunction, 1000, resolve);
}
};
return new Promise(handlerFunction);
}
let c = [];
setTimeout(function() {
c.push("test");
}, 2000);
get().then(value => {
console.log(value);
});
For more information look into javascript polling article
You could try this solution. Since JS needs to free itself to download the images, I use await within an asynchronous function and an asynchronous call to wake up JS after a delay
private async onBeforeDoingSomething() : void {
await this.delay(1000);
console.log("All images are loaded");
}
private delay (ms : number = 500) : Promise<number> {
return new Promise((resolve,reject) => {
const t = setTimeout( () => this.areImgsLoaded(resolve), ms);
});
}
private async areImgsLoaded (resolve) {
let reload = false;
const img = document.querySelectorAll('img');
console.log("total of images: ",img.length);
for (let i = 0; i < img.length; i++){
if (!img[i]["complete"]) {
console.log("img not load yet");
reload = true;
break;
}
}
if (reload) {
await this.delay();
}
resolve();
}
Use setInterval to check every second. Run this script to understand.
let c = [];
function get(){
return new Promise((resolve) => {
var i = setInterval(function(){
if(c.length > 0){
resolve(c.shift());
clearInterval(i);
}
}, 1000);
});
}
setTimeout(function(){
c.push('test');
}, 2000);
get().then((value) => {
console.log(value);
});

Passing rest of the codes as callback function

In a middle of already completed javascript function, you want to call a new async function. So you need to pass, "rest of the code" as a callback function as parameter to this new function.
function sample() {
alert("a bunch of codes");
alert("another a bunch of codes");
}
I have to change the function as below.
function sample() {
alert("a bunch of codes");
var cb = function () {
alert("another a bunch of codes");
};
newFunction(cb);
}
What if I want to add another function that has to wait first one ? Then I got numerous multiple levels of callback functions to the wait another..
So what is the best practice on ES5 ?
In ES5, just like you said you have to nest multiple callbacks inside each other.
Example:
function myFunction2(){
console.log(2);
let myFunction = () => {
console.log(1);
}
myFunction();
}
myFunction2();
// OUTPUT
// 2
// 1
ES6 also provides a new alternative, promises.
Example:
let myPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 250);
});
console.log(2);
myPromise.then((successMessage) => {
console.log(successMessage);
});
// OUTPUT
// 2
// 1
ES8 has provides an even better alternative(although it is just syntactic sugar based on promises) but you can use async functions with await.
Example:
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
add1(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
});
Keep in mind though, that you probably need to use Babel to transpile your js in order to be compatible with all browsers.

Categories

Resources