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() is not exactly a perfect tool for the job, but it's easy enough to wrap it into a promise: const awaitTimeout = delay => new Promise(resolve => setTimeout(resolve, delay)); awaitTimeout(300).
We can wrap setTimeout in a promise by using the then() method to return a Promise. The then() method takes upto two arguments that are callback functions for the success and failure conditions of the Promise. This function returns a promise.
Use setTimeout() to call the promise's resolve function with the passed value after the specified delay .
setTimeout(..., 0) is called before Promise.
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With