Calling promise method with recursion is going for infinite - javascript

I am calling a method a which is returning a promise and inside it I am calling a method which do some operation and update the count variable. I want all the promise get finish once the count is done but it is not stopping after reaching the value 10.
var count = 0;
function a(p){
return new Promise((resolve, reject) =>{
console.log(count);
if(count == 10) {console.log('sdfsdfsd'); resolve(Date.now()); }
callee().then(() => { count++; a(); } )
})
}
function callee(){ return new Promise((resolve) => resolve())}
a(1).then((res) => console.log(res)).catch((res) => console.log(res));

// So you have a function `foo` which returns a promise eventually resolved, you want to write a function
// `bar` that will call this function n times, waitinng between each call for the returned promise to be
// resolved. This function will itself return a promise
// this function returns a promise which is resolved after one second
const foo = () => new Promise(resolve => setTimeout(resolve, 1000));
// Recursively call the foo function until 0 is reached.
// This will actually create a chain of promises which settle after one second.
// It also uses the fact that if you return a promise `a` in the `then` handler the returned
// promise `b` will only settle when `a` is resolved.
const bar = n => {
if (n === 0) return Promise.resolve();
return foo().then(() => bar(n-1));
};
bar(10).then(() => console.log("done"));

var count = 0;
function a(p) {
return new Promise((resolve, reject) => {
console.log(count);
if (count == 10) { console.log('sdfsdfsd'); return resolve(Date.now()); }
return callee().then(() => {
count++;
return resolve(a());
})
})
}
function callee() { return new Promise((resolve) => resolve()) }
a(1).then((res) => console.log("res",res)).catch((res) => console.log(res))

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>

Is the resolve function necessary for promise constructor?

Consider the following promise array
const promiseArray = [1, 2, 3].map(num =>
new Promise(async resolve => {
while (num > 0) {
await foo();
num--;
}
await bar(num);
resolve(); // No value to return
})
);
const a = Promise.all(promiseArray);
Is the resolve function necessary?
Can we omit it and turn the promise into something like this?
const promiseArray = [1, 2, 3].map(num =>
new Promise(async () => {
while (num > 0) {
await foo();
num--;
}
await bar(num);
})
);
const a = Promise.all(promiseArray);
Yes, calling resolve or reject is necessary when using the new Promise constructor, otherwise the promise will stay pending.
However, in your case, you shouldn't be using new Promise at all:
const promiseArray = [1, 2, 3].map(async num => {
while (num > 0) {
await foo();
num--;
}
await bar(num);
});
const a = Promise.all(promiseArray);
Can we omit it and turn the promise into something like this?
No, you cannot. Without calling resolve(), your new Promise() will never resolve. When you then call:
const a = Promise.all(promiseArray);
None of the promises in promiseArray will ever resolve. So, the promise a will never resolve. So, none of this will be of any use and you will have no way of knowing when everything is done executing.
It doesn't appear you really need to wrap anything in a promise here. You can just do this:
async function runLoop() {
for (let i = 1; i <= 3; i++) {
let num = i;
while (num > 0) {
await foo();
num--;
}
await bar(num); // note this will always be bar(0)
}
}
runLoop().then(() => {
console.log("all done");
}).catch(err => {
console.log(err);
});
Or, if you want your separate loops to run in parallel, you can do this:
const promiseArray = [1, 2, 3].map(async num => {
while (num > 0) {
await foo();
num--;
}
await bar(num); // note this will always be bar(0)
});
Promise.all(promiseArray).then(() => {
console.log("all done");
}).catch(err => {
console.log(err);
});
When you create the promise using constructor, first parameter should be called to resolve the promise, the second parameter(if present) to the function should be called to reject the promise. If you are not calling any of the params then promise will be in the pending state always. More information
I think you are confused with async/await and promises.
When you specify async function it will always return resolved promise unless you throw an error inside function which is not caught.
For example:
async function hello(){ return 1}
hello().then((val) => console.log(val));
The same example you can implement using promises
let promise = return new Promise((resolve) => return resolve(1));
promise.then((val) => console.log(val));

Async/Await error

Can someone help me understand why the following code prints blank? I'm expecting it to print "done" as I assume that the await will make the program wait for the promise to resolve.
Thanks for the help!
var y = '';
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // wait till the promise resolves (*)
y = result;
}
f().then(console.log(y));
You must pass a callback function to then, not call console.log immediately and pass its return value:
f().then(() => console.log(y));
Of course the code would be much better if you didn't use a global variable but rather returned the value from the async function so that the promise fulfilled with it:
async function f() {
const promise = new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
});
await promise;
const result = "done!";
return result;
}
f().then((y) => console.log(y));

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);
});

Use Promise to wait until polled condition is satisfied

I need to create a JavaScript Promise that will not resolve until a specific condition is true. Let's say I have a 3rd party library, and I need to wait until a certain data condition exists within that library.
The scenario I am interested in is one where there is no way to know when this condition is satisfied other than by simply polling.
I can create a promise that waits on it - and this code works, but is there a better or more concise approach to this problem?
function ensureFooIsSet() {
return new Promise(function (resolve, reject) {
waitForFoo(resolve);
});
}
function waitForFoo(resolve) {
if (!lib.foo) {
setTimeout(waitForFoo.bind(this, resolve), 30);
} else {
resolve();
}
}
Usage:
ensureFooIsSet().then(function(){
...
});
I would normally implement a max poll time, but didn't want that to cloud the issue here.
A small variation would be to use a named IIFE so that your code is a little more concise and avoids polluting the external scope:
function ensureFooIsSet() {
return new Promise(function (resolve, reject) {
(function waitForFoo(){
if (lib.foo) return resolve();
setTimeout(waitForFoo, 30);
})();
});
}
Here's a waitFor function that I use quite a bit. You pass it a function, and it checks and waits until the function returns a truthy value, or until it times out.
This is a simple version which illustrates what the function does, but you might want to use the full version, added further in the answer
let sleep = ms => new Promise(r => setTimeout(r, ms));
let waitFor = async function waitFor(f){
while(!f()) await sleep(1000);
return f();
};
Example usages:
wait for an element to exist, then assign it to a variable
let bed = await waitFor(() => document.getElementById('bedId'))
if(!bed) doSomeErrorHandling();
wait for a variable to be truthy
await waitFor(() => el.loaded)
wait for some test to be true
await waitFor(() => video.currentTime > 21)
add a specific timeout to stop waiting
await waitFor(() => video.currentTime > 21, 60*1000)
pass it some other test function
if(await waitFor(someTest)) console.log('test passed')
else console.log("test didn't pass after 20 seconds")
Full Version:
This version takes cares of more cases than the simple version, null, undefined, empty array, etc., has a timeout, a frequency can be passed as an argument, and logs to the console what it is doing with some nice colors
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));}
/**
* Waits for the test function to return a truthy value
* example usage:
* wait for an element to exist, then save it to a variable
* let el = await waitFor(() => document.querySelector('#el_id')))
* timeout_ms and frequency are optional parameters
*/
async function waitFor(test, timeout_ms = 20 * 1000, frequency = 200) {
if (typeof (test) != "function") throw new Error("test should be a function in waitFor(test, [timeout_ms], [frequency])")
if (typeof (timeout_ms) != "number") throw new Error("timeout argument should be a number in waitFor(test, [timeout_ms], [frequency])");
if (typeof (frequency) != "number") throw new Error("frequency argument should be a number in waitFor(test, [timeout_ms], [frequency])");
let logPassed = () => console.log('Passed: ', test);
let logTimedout = () => console.log('%c' + 'Timeout : ' + test, 'color:#cc2900');
let last = Date.now();
let logWaiting = () => {
if(Date.now() - last > 1000) {
last = Date.now();
console.log('%c' + 'waiting for: ' + test, 'color:#809fff');
}
}
let endTime = Date.now() + timeout_ms;
let isNotTruthy = (val) => val === undefined || val === false || val === null || val.length === 0; // for non arrays, length is undefined, so != 0
let result = test();
while (isNotTruthy(result)) {
if (Date.now() > endTime) {
logTimedout();
return false;
}
logWaiting();
await sleep(frequency);
result = test();
}
logPassed();
return result;
}
Is there a more concise approach to this problem?
Well, with that waitForFoo function you don't need an anonymous function in your constructor at all:
function ensureFooIsSet() {
return new Promise(waitForFoo);
}
To avoid polluting the scope, I would recommend to either wrap both in an IIFE or to move the waitForFoo function inside the ensureFooIsSet scope:
function ensureFooIsSet(timeout) {
var start = Date.now();
return new Promise(waitForFoo);
function waitForFoo(resolve, reject) {
if (window.lib && window.lib.foo)
resolve(window.lib.foo);
else if (timeout && (Date.now() - start) >= timeout)
reject(new Error("timeout"));
else
setTimeout(waitForFoo.bind(this, resolve, reject), 30);
}
}
Alternatively, to avoid the binding that is needed to pass around resolve and reject you could move it inside the Promise constructor callback like #DenysSéguret suggested.
Is there a better approach?
Like #BenjaminGruenbaum commented, you could watch the .foo property to be assigned, e.g. using a setter:
function waitFor(obj, prop, timeout, expected) {
if (!obj) return Promise.reject(new TypeError("waitFor expects an object"));
if (!expected) expected = Boolean;
var value = obj[prop];
if (expected(value)) return Promise.resolve(value);
return new Promise(function(resolve, reject) {
if (timeout)
timeout = setTimeout(function() {
Object.defineProperty(obj, prop, {value: value, writable:true});
reject(new Error("waitFor timed out"));
}, timeout);
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
get: function() { return value; },
set: function(v) {
if (expected(v)) {
if (timeout) cancelTimeout(timeout);
Object.defineProperty(obj, prop, {value: v, writable:true});
resolve(v);
} else {
value = v;
}
}
});
});
// could be shortened a bit using "native" .finally and .timeout Promise methods
}
You can use it like waitFor(lib, "foo", 5000).
Here's a utility function using async/await and default ES6 promises. The promiseFunction is an async function (or just a function that returns a promise) that returns a truthy value if the requirement is fulfilled (example below).
const promisePoll = (promiseFunction, { pollIntervalMs = 2000 } = {}) => {
const startPoll = async resolve => {
const startTime = new Date()
const result = await promiseFunction()
if (result) return resolve()
const timeUntilNext = Math.max(pollIntervalMs - (new Date() - startTime), 0)
setTimeout(() => startPoll(resolve), timeUntilNext)
}
return new Promise(startPoll)
}
Example usage:
// async function which returns truthy if done
const checkIfOrderDoneAsync = async (orderID) => {
const order = await axios.get(`/order/${orderID}`)
return order.isDone
}
// can also use a sync function if you return a resolved promise
const checkIfOrderDoneSync = order => {
return Promise.resolve(order.isDone)
}
const doStuff = () => {
await promisePoll(() => checkIfOrderDone(orderID))
// will wait until the poll result is truthy before
// continuing to execute code
somethingElse()
}
function getReportURL(reportID) {
return () => viewReportsStatus(reportID)
.then(res => JSON.parse(res.body).d.url);
}
function pollForUrl(pollFnThatReturnsAPromise, target) {
if (target) return P.resolve(target);
return pollFnThatReturnsAPromise().then(someOrNone => pollForUrl(pollFnThatReturnsAPromise, someOrNone));
}
pollForUrl(getReportURL(id), null);

Categories

Resources