How to control setTimeout with promises - javascript

var functionsArray = [
function() {
setTimeout(function() {
console.log(1);
}, 100);
},
function() {
setTimeout(function() {
console.log(2);
}, 200);
},
function() {
setTimeout(function() {
console.log(3);
}, 10);
}
],
Say I have an array of functions like above(the number of functions in it is not known). I want to write a function which takes this array as parameter and executes them in sequence. In the example above, I want it to log 1,2,3 in the sequence. Since Promise.all does not guarantee the order of execution, is it possible to achieve this without callback hell?

You can't get a promise from a function that just calls setTimeout - it needs some help, e.g.:
function after(n, f) {
return () => new Promise(resolve => {
setTimeout(() => {
resolve(f());
}, n);
});
}
with usage:
var functionsArray = [
after(100, () => console.log(1)),
after(200, () => console.log(2)),
after( 10, () => console.log(3)),
];
With that array you can then just await each function in turn:
for (let f of functionsArray) {
await f();
}

You can write an simple setTimeoutPromise function:
function timeoutPromise(time = 0){
return new Promise(resolve => setTimeout(resolve, time)
}
await timeoutPromise(10);
console.log('waited 10 sec')
timeoutPromise(20).then(() => console.log('waited 20 sec')
//make a new array as you like
Promise.all([
timeoutPromise(100).then(() => console.log(1)),
timeoutPromise(200).then(() => console.log(2)),
timeoutPromise(300).then(() => console.log(3))
])

Related

How to debounce a function that return a Promise? [duplicate]

This question already has an answer here:
How to hold a NodeJS application until other promise completes
(1 answer)
Closed 9 months ago.
I have a base async function that returns a Promise
function asyncFoo(): Promise<void> {
return new Promise<void>((resolve, reject) => {
// I'm using here a setTimeout to fake async flow, in reality I'm making a server call
setTimeout(() => {
resolve();
}, 5000);
});
}
I can use my method
const promise = asyncFoo();
promise.then(() => {
console.log('Hello');
});
However if I debounce my function, the result function doesn't return anything, and I can't wait for it to resolve
const debounceFoo = _.debounce(asyncFoo, 100);
debounceFoo(); // undefined
// I would like to do something like
// debounceFoo().then(...) this will not work
How can I debounce the events (aggregate them) that happen during the debounce-time, then execute my asyncFoo(). And finally, act on its resolve callback?
You can use promise to do this:
const debounceAsync = (func, wait) => {
let timerID = -1;
return (...args) => {
clearTimeout(timerID);
const promiseForFunc = new Promise((resolve) => {
timerID = setTimeout(resolve, wait);
});
return promiseForFunc.then(() => func(...args));
};
};
const debounceFoo = debounceAsync(asyncFoo, 100);
debounceFoo().then(() => {
console.log('Hello');
});
The debounced function which return by lodash debounce usually return undefined before first time your func was invoked.
https://github.com/lodash/lodash/blob/2f79053d7bc7c9c9561a30dda202b3dcd2b72b90/debounce.js#L184-L206
Not sure about lodash's implementation but you can write your own debounce as this.
function debounce(func, timeout) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
}
function asyncBar() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Hi");
resolve();
}, 4000);
});
}
const foo = debounce(async () => asyncBar(), 5000);
foo();

How to schedule multiple timeout functions

I have a scenario where I need to schedule multiple timeouts in vanilla JS, it will be something like this below:
const scheduler = {
done: function() {},
schedule: function() {}
};
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(1);
done()
}, 2000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(2);
done()
}, 1000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(3);
done()
}, 3000);
});
it should execute it in such a way that it must print below:
1
2
3
This is a nice use case of promises.
These objects allow the abstraction over asynchronous operations, and fortunately, can be chained.
const scheduler = {
last: Promise.resolve(),
schedule(cb){
this.last = this.last.then(() => new Promise(cb))
}
}
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(1);
done()
}, 2000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(2);
done()
}, 1000);
});
scheduler.schedule(function(done) {
setTimeout(() => {
console.log(3);
done()
}, 3000);
});
However, if you use this with promises you obtained from elsewhere , it can quickly turn into the Explicit Promise construction antipattern, so, for a more general code, move the new Promise() part out of the schedule method:
const scheduler = {
last: Promise.resolve(),
schedule(cb){
this.last = this.last.then(cb)
}
}
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(1);
done()
}, 2000);
})
});
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(2);
done()
}, 1000);
})
});
scheduler.schedule(function() {
return new Promise(done => {
setTimeout(() => {
console.log(3);
done()
}, 3000);
})
});
You would need a que for the timeouts, eg:
const scheduler = {
done:function(){}, // Not sure what this function is supposed to do
schedule:function(callback, timeout) {
scheduler.que.push([callback, timeout]); // Add the function along with its wait time
if(scheduler.que.length == 1) scheduler.startQue(); // If the que was previously empty, start the execution of the que
},
startQue() {
setTimeout(() => {
scheduler.que[0][0](); // run the callback timeout
scheduler.que.shift(); // Remove the just ran timeout
if(scheduler.que.length >= 1) scheduler.startQue(); // Start another timeout if we still have some in the que
}, scheduler.que[0][1]);
},
que: []
};
scheduler.schedule(() => console.log(1), 2000); // Wait for 2 seconds to print
scheduler.schedule(() => console.log(2), 1000); // Wait 1 second after the first one printed
scheduler.schedule(() => console.log(3), 3000); // Wait another 3 seconds after the second one
const scheduler = {
done: function (cb) {
cb();
this.timeouts.shift();
this.timeouts[0] && this.timeouts[0]();
},
schedule: function (cb, ms) {
const timeout = () => setTimeout(this.done.bind(this), ms, cb);
this.timeouts.push(timeout) == 1 && timeout();
},
timeouts: [],
};
scheduler.schedule(() => console.log('1'), 2000);
scheduler.schedule(() => console.log('2'), 1000);
scheduler.schedule(() => console.log('3'), 3000);

Convert setInterval to promise

Hello I'm new to javascript and wondering if there is a way to covnert below setInterval thingy into a promise so that .then could be used instead of the callback. Any help?
My ideas:
With a setTimeout I could resolve after a fixed time. But I'm not getting any ideas dealing with setInterval...
function alert_above(scrip, price, callback) {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
callback();
clearInterval(intvl);
}
}, 1000);
return intvl;
}
You can create a promise function that resolves asynchronously. Read more about Promise Here
function myInterval() {
return new Promise(resolve => {
const intId = setInterval(() => {
clearInterval(intId);
resolve();
}, 1000)
})
}
myInterval().then(() => {
console.log('Called after 1 second');
})
I think you could wrap into a new Promise like :
function promisifySetInterval(time) {
var defer = new Promise(resolve => {
let counter = 0
var intvl = setInterval(() => {
if (counter > time) {
resolve('hey');
clearInterval(intvl);
} else {
counter += 1000
}
}, 1000);
})
return defer;
}
promisifySetInterval(2000).then(param => {
console.log('hey', param)
})
And for youre case something like this :
function alert_above(scrip, price) {
var defer = new Promise(resolve => {
var intvl = setInterval(() => {
if (get_last_price(scrip) > price) {
resolve('hey');
clearInterval(intvl);
}
}, 1000);
})
return defer;
}
alert_above().then(param => {
console.log('hey', param)
})
Note: poll will be executed without delay the first time, which is different from the native setInterval.
Q: Why is poll based on setTimeout not setInterval?
A: Please see Execute the setInterval function without delay the first time.
Implementation:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
// Promisify setInterval
const poll = async (interval, times, cb, ...args) => {
let result
const resolve = value => (times = 0) || (result = value)
const reject = reason => (times = 0) || (result = Promise.reject(reason))
await (async function basePoll() {
if (times > 0) {
const _result = await cb(...args, resolve, reject)
if (times) {
result = _result
--times && (await pause(interval, basePoll))
}
}
})()
return result
}
Tests:
import ordinal from 'ordinal'
// Test 1
poll(1000, 3, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 2
let times = 0
poll(1000, 5, resolve => {
console.log(`${ordinal(++times)} time`)
times === 3 && resolve('resolved')
}).then(value => console.log(value))
// Test 3
let times = 0
poll(1000, 5, (resolve, reject) => {
console.log(`${ordinal(++times)} time`)
times === 3 && reject('rejected')
}).catch(error => console.error(error))

One Promise calling an unknown (dynamic) number of async methods in sequence [duplicate]

This question already has answers here:
ES6 promises with timeout interval
(6 answers)
Closed 5 years ago.
I want to create one Promise that, internally, calls a series of asynchronous methods in sequence and returns the results of these methods, concatenated in an ordered Array, once the last method returns.
I was trying this:
const getArray = function (thisArray) {
return new Promise ( function (resolve, reject) {
if (thisArray.length < 3) {
setTimeout(function() {
console.log('adding element');
thisArray.push(thisArray.length);
getArray(thisArray);
}, 1000);
} else {
console.log(thisArray);
console.log('resolving');
resolve(thisArray);
}
});
}
getArray([]).then(function(data) {
console.log('thened');
console.log(data);
})
but it doesn't ever calls the then(). How can I do this?
You're not far off at all, you just need to be sure to call resolve for each new Promise. If you're still building the array, you pass the resulting promise from your recursive getArray call; otherwise, you pass the array:
const getArray = function(thisArray) {
return new Promise((resolve, reject) => {
if (thisArray.length >= 3) {
// All done
resolve(thisArray);
} else {
// Do something async...
setTimeout(() => {
// ...add the result
console.log('adding element');
thisArray.push(thisArray.length);
// ...and recurse
resolve(getArray(thisArray));
}, 1000);
}
});
}
getArray([]).then(data => {
console.log('done');
console.log(data);
});
That's a bit cleaner if you separate the function doing the async work from getArray, and ensure that the function doing the async work returns a promise. For our purposes, we can just wrap setTimeout in a Promise wrapper:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
then:
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
});
};
Live copy:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
return getArray(thisArray);
}, 1000);
};
getArray([]).then(data => {
console.log('done');
console.log(data);
});

How to update Promise.all with new array?

In my code below, I would like to execute a, b and c but then a1 since a1 is added within a. However, it doesn't look like Promise.all is updated with the new a1 added. What's the best way to do this? Is there Promise all update? I try not to do await a1 within a.
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1);
resolve(1);
}, 2000);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 4000);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 6000);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 2000);
});
}
arr = [a(), b(), c()];
(async function run() {
await Promise.all(arr);
})();
console.log('Done');
For a start, you push a1 where I think you'd want to push a1() - otherwise you're pushing a function, not a promise
But that won't change anything, because the array passed to Promise.all is (in every library I've seen) copied (using Array#slice) so any changes to the passed in array won't be "visible" to the Promise.all (internal) code
But, you can create a function that recursively calls Promise.all on the array until the length of the results is equal to the (current, new) length of the array
var recursiveAll = a => Promise.all(a).then(r => r.length == a.length ? r : recursiveAll(a));
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1());
resolve(1);
}, 200);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 400);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 600);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 200);
});
}
arr = [a(), b(), c()];
(async function run() {
let results = await recursiveAll(arr);
console.log(results);
})();
Interesting question...
You may indeed modify Promise.all() on the run by replacing a promise with another one even if the newly inserted one resolves the latest.
In the below example promise three will replace itself with a new promise which will resolve in an additional 2000ms later and Promise.all() will wait until it to resolves to include it's resolving value to the array provided to the .then() stage.
var fun = _ => new Promise((v,x) => setTimeout(v,2000,5)),
one = new Promise((v,x) => setTimeout(v,1000,1)),
two = new Promise((v,x) => setTimeout(v,2000,2)),
three = new Promise((v,x) => setTimeout(v,3000,fun())),
four = new Promise((v,x) => setTimeout(v,4000,4));
Promise.all([one,two,three,four])
.then(a => console.log(a));
Please wait for 5 seconds to see the result.

Categories

Resources