Calling a function foo
that does “something” after N milliseconds set by timeout. If foo
is called again before timeout expires I first cancel timeout - then set new. Simple enough using a global variable for a timer.
Usually that is it, but then I found that in some cases I need to wait for the “something” to finish and then do some follow-up things. From that I assume a promise is the way to go.
Or in more general terms:
How to return a promise from a promise by a timeout that can be cancelled.
After some meddling I have this. Look at sample of how I use it with a second promise:
const promise_time = (ms, old) => {
let timeout, p = { pending: true };
if (old && old.pending)
old.abort();
p.wait = new Promise(resolve => {
timeout = setTimeout(() => {
p.pending = false;
resolve();
}, ms);
});
p.abort = () => {
clearTimeout(timeout);
p.pending = false;
};
return p;
};
Using it in combination with a second promise as shown below.
But it feels somewhat clumsy and overly complex; but I do not manage to see for myself if it is. Need fresh eyes :P.
Is there a better way to do this? (rather likely)
Have looked into AbortController
but did not find a way that seemed more clean, rather the opposite.
Sample usage:
Below is a dummy example. Just type something in the text box. If keydown delay is < 1s the timeout is cancelled and new set. Else it fires and resolves promise.
The button is meant to resemble the normal procedure, i.e. just do it after a delay.
const promise_time = (ms, old) => {
let timeout, p = { pending: true };
if (old && old.pending)
old.abort();
p.wait = new Promise(resolve => {
timeout = setTimeout(() => {
p.pending = false;
resolve();
}, ms);
});
p.abort = () => {
clearTimeout(timeout);
p.pending = false;
};
return p;
};
let timed;
const do_timed_thing = v => {
// Initiate timer + potentially clear old
timed = promise_time(1000, timed);
return new Promise(resolve => {
timed.wait.then(() => {
// garbageify object
timed = null;
add_li('timed val “' + v + '”');
resolve();
});
});
};
const add_li = txt => {
let li = document.createElement('LI');
li.textContent = (new Date()).toLocaleTimeString() + ' ' + txt;
document.getElementById('ul').appendChild(li);
};
const txt = document.getElementById('txt');
txt.addEventListener('input', () => {
do_timed_thing(txt.value).then(() => {
txt.value = '';
add_li('cleared input');
});
});
document.getElementById('nop')
.addEventListener('click',() => {
do_timed_thing('Only something');
});
txt.focus();
<div style="display: grid">
<p>Max 1 sec between keystrokes; Do something + something else.</p>
<input id="txt" type="text" placeholder="Do something + something else" />
<hr />
<button id="nop">Do something (after 1 sec) and only that</button>
<ul id="ul"></ul>
</div>