I am struggling to make a simple waiting function in my program. I want to use promises and async await if possible. What I have so far:
function waitForCondition(conditionObj) {
var start_time = new Date().getTime()
function checkFlag() {
if (conditionObj.arg == conditionObj.test) {
console.log('met');
return new Promise(resolve => setTimeout(resolve, 1));
} else if (new Date() > start_time + 3000) {
console.log('not met, time out');
return new Promise(resolve => setTimeout(resolve, 1));
} else {
window.setTimeout(checkFlag, 1000);
}
}
checkFlag();
}
async function run() {
console.log('before');
await waitForCondition({arg: '1', test: '1'})
console.log('after');
}
run();
It should check every 1 second for a maximum time of 3 seconds. The console should look like this:
'before'
'met'
'after'
You have to return a promise:
function waitForCondition(conditionObj) {
return new Promise(resolve => {
var start_time = Date.now();
function checkFlag() {
if (conditionObj.arg == conditionObj.test) {
console.log('met');
resolve();
} else if (Date.now() > start_time + 3000) {
console.log('not met, time out');
resolve();
} else {
window.setTimeout(checkFlag, 1000);
}
}
checkFlag();
});
}
async function run() {
console.log('before');
await waitForCondition({arg: '1', test: '1'})
console.log('after');
}
run();
I refactored your code a bit. To get the current time, use Date.now(). And you should be OK with calling resolve without a timeout of 1 millisecond.
I believe what you are really looking for is
function waitForCondition(conditionObj) {
var start_time = new Date().getTime()
async function checkFlag() {
if (conditionObj.arg == conditionObj.test) {
console.log('met');
// return something?
} else if (new Date() > start_time + 3000) {
console.log('not met, time out');
// throw some error?
} else {
await new Promise(resolve => setTimeout(resolve, 1000));
return checkFlag();
}
}
return checkFlag();
}
or with a loop instead of the recursion
async function waitForCondition(conditionObj) {
var start_time = new Date().getTime()
while (true) {
if (conditionObj.arg == conditionObj.test) {
console.log('met');
break; // or return
}
if (new Date() > start_time + 3000) {
console.log('not met, time out');
break; // or throw
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
Your function waitForCondition implicitely returns undefined. try to add a return statment at the end of it:
return checkFlag();
And as mentionned in comment, there one condition where checkFlag() returns undefined that you might want to handle by returning a Promise.
Related
In a browser extension I'm trying to:
find a button
update its text every second for 10 seconds
invoke submit
Simple enough, but unfortunately I'm a JavaScript novice.
I'm clueless: Why does the code below not reach line #15 (after await)?
const Timeout = 10000;
const CountdownStep = 1000;
async function scheduleSubmit(node, timeout) {
originalTextContent = node.textContent;
while (timeout > 0) {
console.log(`Timeout: ${timeout}`);
try {
await new Promise((resolve => setTimeout(() => {
console.log(`[Promise] Timeout: ${timeout}`);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= CountdownStep;
console.log(`[Promise] Timeout: ${timeout}`);
}, CountdownStep)));
console.log('Hello? Helloooooooo??');
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), Timeout);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
If you extract a simple delay helper that returns a Promise that resolves after the specified amount of time you code becomes way more readable. Not to speak you can test and debug your code separately to identify the issue.
const SUBMIT_TIMEOUT = 10000;
const COUNT_DOWN_STEP = 1000;
function delay(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout))
}
async function scheduleSubmit(node, timeout) {
const originalTextContent = node.textContent;
while (timeout > 0) {
try {
await delay(COUNT_DOWN_STEP);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= COUNT_DOWN_STEP;
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), SUBMIT_TIMEOUT);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
You just need to resolve the Promise calling resolve():
const Timeout = 10000;
const CountdownStep = 1000;
async function scheduleSubmit(node, timeout) {
originalTextContent = node.textContent;
while (timeout > 0) {
console.log(`Timeout: ${timeout}`);
try {
await new Promise((resolve => setTimeout(() => {
console.log(`[Promise] Timeout: ${timeout}`);
node.textContent = `${originalTextContent} (${timeout / 1000})`;
timeout -= CountdownStep;
console.log(`[Promise] Timeout: ${timeout}`);
resolve();
}, CountdownStep)));
console.log('Hello? Helloooooooo??');
} catch (err) {
log(`Error: ${err}`);
}
}
node.submit();
}
scheduleSubmit(document.getElementById('foo'), Timeout);
<html><body>
<button type="button" id="foo">Run</button>
</body></html>
Docs: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise
Is there a way to use setTimeout without using setTimeout inbuilt function?
I don't want to use setInterval or clearInterval either or window.use. I have gone through multiple blogs, but all those use window, setInterval or clearInterval.
For example, the below code works, but I dont want to have window.
const setTimeouts = [];
function customSetTimeout(cb, interval) {
const now = window.performance.now();
const index = setTimeouts.length;
setTimeouts[index] = () => {
cb();
};
setTimeouts[index].active = true;
const handleMessage = (evt) => {
if (evt.data === index) {
if (window.performance.now() - now >= interval) {
window.removeEventListener('message', handleMessage);
if (setTimeouts[index].active) {
setTimeouts[index]();
}
} else {
window.postMessage(index, '*');
}
}
};
window.addEventListener('message', handleMessage);
window.postMessage(index, '*');
return index;
}
const setIntervals = [];
function customSetInterval(cb, interval) {
const intervalId = setIntervals.length;
setIntervals[intervalId] = function () {
if (setIntervals[intervalId].active) {
cb();
customSetTimeout(setIntervals[intervalId], interval);
}
};
setIntervals[intervalId].active = true;
customSetTimeout(setIntervals[intervalId], interval);
return intervalId;
}
function customClearInterval(intervalId) {
if (setIntervals[intervalId]) {
setIntervals[intervalId].active = false;
}
}
console.log("1");
customSetTimeout(function() {
console.log('3s');
}, 3000);
console.log("2");
=======================================
Alternate solution:
But here, again i dont want to use clearInterval and setInterval
var setMyTimeOut = function(foo,timeOut){
console.log('inside time out');
var timer;
var currentTime = new Date().getTime();
var blah=()=>{
if (new Date().getTime() >= currentTime + timeOut) {
console.log('clear interval if');
clearInterval(timer);
foo()
}
console.log('clear interval else');
}
timer= setInterval(blah, 100);
}
console.log("1");
setMyTimeOut(function() {
console.log('3s');
}, 3000);
console.log("2");
Is there way to achieve the same but without the use setInterval and clearInterval?
I use here the requestAnimationFrame with performance.now().
Its not super exact (well setTimeout neither), but it do the work.
function sleep(delay, cb) {
function check(time, delay) {
if(time >= delay) {
cb("done");
return;
}
time = performance.now();
requestAnimationFrame(check.bind(null, time,delay))
}
check(performance.now(), delay + performance.now());
}
sleep(4000, ()=> {
console.log("sleep done");
})
console.log("i do not block the main thread");
You can use a while loop which checks a set time against the current time:
const startTime = new Date().getTime() + 3000;
let currentTime = new Date().getTime();
function customTimeout() {
while (startTime > currentTime) {
currentTime = new Date().getTime();
}
return console.log('3 Seconds')
};
customTimeout();
I have a function say myMainFunction that is called from a client, that in turn calls mypromisified function.
Scenario:
mypromisified function can fail intermittently and I need to call this function with a delay (at an exponential increase) until success or until max no of tries reached.
What I have so far
The following code illustrates my scenario and repeats itself until success, but it tries indefinitely and not until certain count is reached
// called once from the client
myMainFuntion();
function rejectDelay(delay, reason) {
// call main function at a delayed interval until success
// but would want to call this only a limited no of times
setTimeout(() => {
myMainFuntion(); // calling main function again here but with a delay
}, delay);
}
function myMainFuntion() {
var delay = 100;
var tries = 3;
tryAsync().catch(rejectDelay.bind(null, delay));
}
function tryAsync() {
return new Promise(function(resolve, reject) {
var rand = Math.random();
console.log(rand);
if (rand < 0.8) {
reject(rand);
} else {
resolve();
}
});
}
while loop inside the rejectDelay would certainly not work as the counter would increment even before the actual function inside setInterval is executed, so am unsure as to how to go about this? so...
I tried promisifying the setInterval something like this knowing it will fail :( as it doesnt decrement the counter, but not sure how to get it right either .
function rejectDelay(delay, maximumTries, reason) {
return new Promise(function (resolve, reject) {
console.log(tries + ' remaining');
if (--maximumTries > 0) {
setTimeout(function() {
foo();
}, 500);
}
});
}
function myMainFunction() {
var delay = 100;
var maximumTries = 3;
tryAsync().catch(rejectDelay.bind(null, delay, maximumTries));
}
Using a couple of helper functions I've used a lot, this becomes very easy
The "helpers"
Promise.wait = (time) => new Promise(resolve => setTimeout(resolve, time || 0));
Promise.retry = (cont, fn, delay) => fn().catch(err => cont > 0 ? Promise.wait(delay).then(() => Promise.retry(cont - 1, fn, delay)) : Promise.reject('failed'));
The code:
function myMainFuntion() {
var delay = 100;
var tries = 3;
Promise.retry(tries, tryAsync, delay);
}
ES5 versions of the helpers
Promise.wait = function (time) {
return new Promise(function (resolve) {
return setTimeout(resolve, time || 0);
});
};
Promise.retry = function (cont, fn, delay) {
return fn().catch(function (err) {
return cont > 0 ? Promise.wait(delay).then(function () {
return Promise.retry(cont - 1, fn, delay);
}) : Promise.reject('failed');
});
};
A slightly different approach that uses "asynchronous recursion" to call a nested function within a function that returns a promise:
function retry( func, maxTries, delay) {
var reTry = 0;
return new Promise( function(resolve, reject) {
function callFunc() {
try
{
func().then(resolve, function( reason) {
if( ++reTry >= maxTries) {
reject( reason);
}
else {
setTimeout( callFunc,
typeof delay=="function" ? delay( retry) : delay );
}
});
}
catch(e) {
reject(e);
}
}
callFunc();
});
}
// ******* run snippet to test ********
var retryCount = 0;
function getDelay( n) {
// return 100 * n*n + 500; // for example
++ retryCount;
return 100; // for testing
}
function testFunc() {
return Math.random() < 0.8 ? Promise.reject("too many tries") : Promise.resolve( "success");
}
retry( testFunc, 5, getDelay).then(
function(data) { console.log("data: %s, retryCount %s", data, retryCount);},
function(reason){console.log("reason: %s, retryCount %s", reason, retryCount);}
)
I have a promise-returning function that does some async stuff, let's call it functionToRepeat().
I am trying to write the function repeatFunction(amount) , so that it will start the promise, wait for completion, start it again, wait for completion, and so on a given amount of times. This repeatFunction(amount) should also be thenable, so that I can chain other stuff after it's been executed.
Here is my attempt:
function functionToRepeat(){
let action = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("resolved!");
resolve()}
,1000);
})
return action
}
function repeatFunction(amount) {
if(amount==0){
return Promise.resolve();
}
return functionToRepeat().then(function(){
repeatFunction(amount-1);
});
}
repeatFunction(5).then(function(){
console.log("DONE!");
})
This successfully chains my promises (or so it seams, I get one "resolved!" per second in the console).
However the .then() I try to chain after my repeatFunction(5) happens after the 1st promise ends, not after all 5 have ended!
So in my console I get:
resolved!
DONE!
resolved!
resolved!
resolved!
resolved!
What am I doing wrong and what should I change?
How about simply:
function repeat(func, times) {
var promise = Promise.resolve();
while (times-- > 0) promise = promise.then(func);
return promise;
}
When tested with this:
function oneSecond() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("tick");
resolve();
}, 1000);
});
}
repeat(oneSecond, 5).then(function () {
console.log("done");
});
this output is produced over 5 seconds:
tick
tick
tick
tick
tick
done
I think you are almost there, but you have to return the repeatFunction again in the then block of your function to repeat.
return functionToRepeat().then(function(){
return repeatFunction(amount-1);
});
}
If you have a then, but do not return anything, then it will just resolve the upper promise. That is what happened.
You're missing a return when you call repeatFunction(amount-1)
function functionToRepeat(){
let action = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("resolved!");
resolve()}
,1000);
})
return action
}
function repeatFunction(amount) {
if(amount==0){
return Promise.resolve();
}
return functionToRepeat().then(function(){
return repeatFunction(amount-1); // Added return
});
}
repeatFunction(5).then(function(){
console.log("DONE!");
})
https://plnkr.co/edit/93T6B4QkBv0mYS4xPw0a?p=preview
You could use async/await with a simple while loop. This keeps you in Asyncland allows you to continue chaining after the repeated function is done
async function asyncRepeat (f, n) {
while (n-- > 0)
await f()
}
asyncRepeat(functionToRepeat, 5).then(() => {
console.log('done')
})
// some value
// some value
// some value
// some value
// some value
// done
This is trash tho. You're restricted to using a side-effecting function as your f argument to asyncRepeat. So logging to the console works, but what if you actually wanted to do something with that value?
Here's an update to asyncRepeat that allows you to thread a value thru the repeated application of your input function (asyncDouble in this example)
(important changes in bold)
async function asyncRepeat (f, n, x) {
while (n-- > 0)
x = await f(x)
return x
}
function asyncDouble (x) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('x is currently: %d', x)
resolve(x * 2) // resolve an actual value
}, 1000)
})
}
asyncRepeat(asyncDouble, 5, 2).then(result => {
console.log('result: %d', result)
})
// x is currently: 2
// x is currently: 4
// x is currently: 8
// x is currently: 16
// x is currently: 32
// result: 64
I wanted something similar, so I wrote a generic function at (https://repl.it/#turlockmike/BriskLovableLinuxkernel)
function repeat(fn, times) {
if (times == 1) {
return fn()
} else {
return new Promise(function(resolve, reject) {
return fn().then(function() {
return resolve(repeat(fn,times - 1))
})
})
}
}
Usage
function doSomething() {
return new Promise(function(resolve, reject) {
//do something interested here
setTimeout(function(){
console.log("resolved!");
resolve()}
,1000);
})
}
repeat(doSomething, 5).then(() => {
console.log("all Done!")
})
const http = require('http');
const https = require('https');
const { t, d, r } = require('minimist')(process.argv.slice(2));
const checkRoot = config => {
const { delaySeconds, rootUrl } = config ? config : { delaySeconds: 6 };
const delay = delaySeconds * 1000;
const protocolString = rootUrl.split(':')[0];
const protocol = {
http: http,
https: https,
};
return new Promise(res => {
setTimeout(() => {
protocol[protocolString]
.get(rootUrl, resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
res({ success: data.includes('<!doctype html>') });
});
})
.on('error', err => {
console.log(`Error: ${err.message}`);
res({ success: false });
});
}, delay);
});
};
const repeatChecking = async ({ times, delaySeconds, rootUrl }) => {
let isReady = false;
console.log(
`will try ${times}, and with ${delaySeconds} seconds delay in between for ${rootUrl}`
);
let i = 1;
while (i <= times) {
if (isReady === true) {
break;
}
const res = await checkRoot({ delaySeconds, rootUrl }).then();
isReady = res.success;
console.log(`testing ${i}, status: root ready => ${res.success}`);
i++;
}
if (isReady) {
console.log('Done, root is ready');
return;
}
process.stdout.write('ERROR: root could not be reached\n');
process.exit(1);
};
repeatChecking({ times: t, delaySeconds: d, rootUrl: r });
let loopP = (n, f, ...args) => {
let p = f(...args);
p.then(res => {
if (n - 1) {
loopP(n - 1, f, res);
}
});
};
Where n is the number of iterations, f is the Promise-returning function to be called. Each successive call to f is passed the results of the previous call when it resolves.
For instance...
let addOneP = i => {
console.log(i + 1);
return Promise.resolve(i + 1);
};
loopP(5, addOneP, 0);
// logs:
// 1
// 2
// 3
// 4
// 5
You might find relign useful for this kind of thing. Here's your example written with relign series and relign setTimeout.
const fnToRepeat = () =>
relign.setTimeout(() => console.log("resolved!"), 1000);
relign.series((new Array(5)).fill(fnToRepeat))
.then(() => console.log('done'));
Is it possible to chain setTimout functions to ensure they run after one another?
Three separate approaches listed here:
Manually nest setTimeout() callbacks.
Use a chainable timer object.
Wrap setTimeout() in a promise and chain promises.
Manually Nest setTimeout callbacks
Of course. When the first one fires, just set the next one.
setTimeout(function() {
// do something
setTimeout(function() {
// do second thing
}, 1000);
}, 1000);
Chainable Timer Object
You can also make yourself a little utility object that will let you literally chain things which would let you chain calls like this:
delay(fn1, 400).delay(fn2, 500).delay(fn3, 800);
function delay(fn, t) {
// private instance variables
var queue = [], self, timer;
function schedule(fn, t) {
timer = setTimeout(function() {
timer = null;
fn();
if (queue.length) {
var item = queue.shift();
schedule(item.fn, item.t);
}
}, t);
}
self = {
delay: function(fn, t) {
// if already queuing things or running a timer,
// then just add to the queue
if (queue.length || timer) {
queue.push({fn: fn, t: t});
} else {
// no queue or timer yet, so schedule the timer
schedule(fn, t);
}
return self;
},
cancel: function() {
clearTimeout(timer);
queue = [];
return self;
}
};
return self.delay(fn, t);
}
function log(args) {
var str = "";
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] === "object") {
str += JSON.stringify(arguments[i]);
} else {
str += arguments[i];
}
}
var div = document.createElement("div");
div.innerHTML = str;
var target = log.id ? document.getElementById(log.id) : document.body;
target.appendChild(div);
}
function log1() {
log("Message 1");
}
function log2() {
log("Message 2");
}
function log3() {
log("Message 3");
}
var d = delay(log1, 500)
.delay(log2, 700)
.delay(log3, 600)
Wrap setTimeout in a Promise and Chain Promises
Or, since it's now the age of promises in ES6+, here's similar code using promises where we let the promise infrastructure do the queuing and sequencing for us. You can end up with a usage like this:
Promise.delay(fn1, 500).delay(fn2, 700).delay(fn3, 600);
Here's the code behind that:
// utility function for returning a promise that resolves after a delay
function delay(t) {
return new Promise(function (resolve) {
setTimeout(resolve, t);
});
}
Promise.delay = function (fn, t) {
// fn is an optional argument
if (!t) {
t = fn;
fn = function () {};
}
return delay(t).then(fn);
}
Promise.prototype.delay = function (fn, t) {
// return chained promise
return this.then(function () {
return Promise.delay(fn, t);
});
}
function log(args) {
var str = "";
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] === "object") {
str += JSON.stringify(arguments[i]);
} else {
str += arguments[i];
}
}
var div = document.createElement("div");
div.innerHTML = str;
var target = log.id ? document.getElementById(log.id) : document.body;
target.appendChild(div);
}
function log1() {
log("Message 1");
}
function log2() {
log("Message 2");
}
function log3() {
log("Message 3");
}
Promise.delay(log1, 500).delay(log2, 700).delay(log3, 600);
The functions you supply to this version can either by synchonrous or asynchronous (returning a promise).
Inspired by #jfriend00 I demonstrated a shorter version:
Promise.resolve()
.then(() => delay(400))
.then(() => log1())
.then(() => delay(500))
.then(() => log2())
.then(() => delay(800))
.then(() => log3());
function delay(duration) {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
}
function log1() {
console.log("Message 1");
}
function log2() {
console.log("Message 2");
}
function log3() {
console.log("Message 3");
}
If your using Typescript targeting ES6 this is pretty simple with Async Await. This is also very very easy to read and a little upgrade to the promises answer.
//WARNING: this is Typescript source code
//expect to be async
async function timePush(...arr){
function delay(t){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve();
},t)
})
}
//for the length of this array run a delay
//then log, you could always use a callback here
for(let i of arr){
//pass the items delay to delay function
await delay(i.time);
console.log(i.text)
}
}
timePush(
{time:1000,text:'hey'},
{time:5000,text:'you'},
{time:1000,text:'guys'}
);
I have encountered the same issue. My solution was to call self by setTimeout, it works.
let a = [[20,1000],[25,5000],[30,2000],[35,4000]];
function test(){
let b = a.shift();
console.log(b[0]);
if(a.length == 0) return;
setTimeout(test,b[1]);
}
the second element in array a is time to be delayed
Using async / await with #Penny Liu example:
(async() => {
await delay(400)
log1()
await delay(500)
log2()
await delay(800)
log3()
})()
async function delay(duration) {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
}
function log1() {
console.log("Message 1");
}
function log2() {
console.log("Message 2");
}
function log3() {
console.log("Message 3");
}