stop await setTimeOut function - javascript

I have a Typescript function called delay() that is invoked in async/await mode.
play(){
(async () => {
await this.delay(90000);
this.player.play();
})();
}
delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Is there a way to "kill/interrupt" the setTimeout before complete the 90 seconds and start the counting again when the "play" function is invoked again?

You probably want something like
const delay = (ms: number) => {
let id;
const promise = new Promise(resolve => {
id = setTimeout(resolve, ms);
});
return {
id, promise
};
}
const play = () => {
const { id, promise } = delay(1000);
promise.then(() => console.log("Promise called"));
// Or call this to cancel
clearTimeout(id);
};
But I'd implement this in another way:
const Timer = (ms: number) => {
let id: number;
const start = () => new Promise(resolve => {
if (id === -1) {
throw new Error('Timer already aborted');
}
id = setTimeout(resolve, ms);
});
const abort = () => {
if (id !== -1 || id === undefined) {
clearTimeout(id);
id = -1;
}
}
return {
start, abort
}
};
const timer = Timer(1000);
timer.start().then(() => console.log("done after 1000ms"));
timer.abort(); // this would abort the operation
// Calling timer.abort() before timer.start() would throw an error as well.

You can read here about Promise cancelation.
I have created a class that can be useful to solve your problem
My class is using the advantages of bluebird library
class Delayer {
constructor() {
this.Promise = require('bluebird');
this.Promise.config({
cancellation: true
})
this.promise = null;
}
delay(ms) {
if(this.promise) {
this.promise.cancel();
}
this.promise = new this.Promise(resolve => setTimeout(resolve, ms));
return this.promise;
}
}
Your code usage
// you need to run `npm install bluebird --save`
const delayer = new Delayer(); // create a new instance of delayer to work with
play(){
(async () => {
await delayer.delay(90000);
this.player.play();
})();
}

setTimeout returns a value. Save the value somewhere, then when you want to "kill/interrupt" it, call clearTimeout on it.

Since your cancel function returns a promise, and ES6 does not allow you to cancel a promise. Promise returns either success or failed value.
To achieve cancelation of promise either use
RxJS observables or third-party libraries like the bluebird

check if this can help.
idea is we create a settimeout reference variable using which we can do clearTimeout, to stop previous settimeout execution.
play = (()=>{let timeoutRef; return((number)=>{if(timeoutRef){clearTimeout(timeoutRef)};timeoutRef = setTimeout(()=>console.log("hello",number),number)})})()
mind the code formatting, I used jsconsole to type in my code.:)

Take a look at "debounced" function.
import { debounce } from “lodash”;
class Player {
constructor() {
this.delayedPlay = debounce(this.play.bind(this), 9000);
}
play() {
/* do stuff here */
}
}
If you make a second call of debouncePlay in less than 90 seconds before the first call, the first call will be canceled and a new timer will start.
Here is a more detailed explanation of the debounce function.

Related

How can I chain functions asynchronously using JavaScript?

I was asked to create such an object called foo that can chain the function log and wait.
For example:
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
In this scenario it prints breakfast first, waits 3 seconds, prints lunch, and then after 3 seconds it prints dinner.
I tried something like this, but it doesn't work. What did I miss?
var foo = {
log: function(text){
console.log(text);
return foo;
},
wait: function(time) {
setTimeout(function() {
return foo;
}, time);
}
}
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
It's always better to use promises. An implementation of this functionality could be;
class Foo {
constructor(){
this.promise = Promise.resolve();
}
log(txt){
this.promise = this.promise.then(_ => console.log(txt))
return this;
}
wait(ms){
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v,ms)));
return this;
}
}
var foo = new Foo();
foo.log("happy").wait(1000).log("new").wait(1000).log("year");
For the record, Redu's excellent answer without the class sugar.
See also
const foo = {
promise: Promise.resolve(),
log(txt) {
this.promise.then(_ => console.log(txt));
return this;
},
wait(ms) {
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v, ms)));
return this;
}
};
// OR
const Foo = (defaultMs = 1000) => {
let promised = Promise.resolve();
return {
log(txt) {
promised.then(_ => console.log(txt));
return this;
},
wait: function(ms) {
promised = promised.then( _=>
new Promise( rs => setTimeout(rs, ms || defaultMs) ) );
return this;
}
};
};
foo.log("Happy").wait(1000).log("new").wait(1000).log("year");
Foo().wait(3000)
.log(`** From Foo ;)`).log(`Happy`).wait().log("new").wait().log("year");
Place the call to wait inside the previous one, and as the last item, like a recursive function.
meals=['breakfast','elevenses','lunch','afternoon tea','dinner','supper'];
c=0;
wait=t=>{setTimeout(function() {
if (c<meals.length) document.write(meals[c++],'<br>');wait(500);
}, t);}
wait(500);
I was inspired by #James 's solution, which is partially wrong because the log messages are in the reverse order, but he does not use Promises. I still think that #Redu 's solution should be the accepted one (after all if you can use Promises, that is perfect), but this one is interesting too in my opinion:
const foo1 = {
incrementalTimeout: 0,
nextActions: [],
log(text) {
const textLog = () => { console.log(text); };
if (this.incrementalTimeout == 0)
textLog();
else
this.nextActions.push(textLog);
return this;
},
wait(time) {
let newObj = {...this, incrementalTimeout: this.incrementalTimeout + time, nextActions: []};
setTimeout(() => { newObj.nextActions.forEach((action) => action()); } , newObj.incrementalTimeout);
return newObj;
}
}
foo1.log('breakfast').wait(1000).log('lunch').wait(3000).log('dinner');
The idea is that I do not immediately log text but I push a lambda with the console.log in an array that is going to be called after the correct timeout expires.
I run all the log and wait operations one after the other, but I keep track of the seconds to wait before executing the actions. Every time a new wait is called, the incrementalTimeout is increased by time. To keep the nextActions belonging to different time frames separated, I return a newObj every time, more or less like #James does.
Shorter, within Promise (not recommended).
Promise.prototype.log = function(txt) {
return this.then(() => console.log(txt))
}
Promise.prototype.wait = function(ms) {
return this.then(() => new Promise(res => setTimeout(res, ms)))
}
var foo = Promise.resolve()
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner')
You can do it without promises:
const foo = {
log(text) {
return {...foo, start: () => {
this.start();
console.log(text);
}};
},
wait(time) {
return {...foo, start: () => {
setTimeout(this.start, time);
}};
},
start() {}
};
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner').start();
The functions foo.log() and foo.wait() return immediately, returning a modified clone of foo. A clone is made using {...foo}, but with the start() function modified so that it calls the caller's this.start() followed by the new operation. When the chain is complete you call start() to start the actions.

JS: Creating a Promise class -- how does function take an arg?

I am looking at an implementation of a Promise class (at bottom). The constructor takes a function, f.
So if I have a regular function I want to pass in:
function myFunc() {
console.log("My Func");
}
If a Promise looks like this :
let promise = new Promise(resolve => {
myFunc();
}
then I am confused as to what f represents now though in the constructor of the class below. It appears as though resolve is the argument, not a function, f.
*Promise Class
class Promise {
constructor(f) {
this.onResolves = [];
this.status = "pending";
const resolve = value => {
debugger
this.status = "resolved";
this.value = value;
this.callOnResolves();
};
f(resolve);
}
callOnResolves() {
if (this.status === "resolved") {
this.onResolves.forEach(onResolve => onResolve(this.value));
this.onResolves.length = 0;
}
}
then(onResolve) {
return new Promise(resolve => {
this.onResolves.push(value => resolve(onResolve(value)));
this.callOnResolves();
});
}
}
f is the function that you pass to the promise's constructor. It takes a resolve parameter, which should eventually be called with the result of the asynchronous work.
Typically, your anonymous function would make use of an api that supports callbacks instead of promises, and in your callback to that api you would call resolve to let the Promise know that your asynchronous work is complete.
This is actually how you could implement an asynchronous sleep method using setTimeout.
function sleep(milliseconds) {
return new Promise(resolve => {
setTimeout(resolve, milliseconds);
});
}
sleep(1000).then(() => console.log(new Date()));

My implementation of debounce axios request left the promise in pending state forever, is there a better way?

I need a simple debounce function with immediate always true.
Without resorting to lodash and with the help of Can someone explain the "debounce" function in Javascript , I implemented it as following,
function debounce(func, wait) {
var timeout;
return function() {
if (!timeout) func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(()=>{timeout = null}, wait);
};
};
It works as expected until I need to debounce axios request. Assumed I have a debounced axios method, I would like the calling method to be as usual, which means my debounced axios method should return promise I believe.
//the calling method should not change
debounced_axios().then(res => {...}).catch(err => {...})
The essence of original debounce implementation is to just run func once in a wait timeframe, but how do I just return one promise in a wait timeframe ?
Then I came up with the following solution
all_timers = {}
function debounce_axios(input, wait) {
return new Promise((resolve, reject) => {
let timer = all_timers.[input] //check if it is a repeated request, pseudo code
if (!timer) {
axios(input).then(res=>{
resolve(res)
}).catch(err => {
reject(err)
})
}
clearTimeout(timer);
timer = setTimeout(()=>{timer = null}, wait);
all_timers[input] = timer
};
};
So the essence of my debounce_axios is to let the promise stay in pending state for the repeated request.Then the calling method debounced_axios().then(res => {...}).catch(err => {...}) does not need to change.
The answer here Are JavaScript forever-pending promises bad? said "There should be no side effect."
But I am still not 100% sure about letting a promise stay in pending forever.
The other question is that Promise Anti patterns suggested not creating unnecessary promise. But in my case creating a new promise seems necessary.
In a nutshell is there a simple to way to debounce axios request (or any request returns promise) ?
But I am still not 100% sure about letting a promise stay in pending forever.
I agree that it's not a good idea. A better approach would be to move the entire promise chain inside the debounced function.
Another option would be to return a cached value when the debounced call does not trigger a new request. This would solve your problem that you always need to return a promise:
function debounce(func, wait) {
var timeout, value;
return function() {
if (!timeout) value = func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = value = null;
}, wait);
return value;
};
}
Of course that would mean that in some cases, multiple then handlers will be called when your request finishes. It depends on your application whether that is a problem or just superfluous work.
The other question is that Promise Anti patterns suggested not creating unnecessary promise. But in my case creating a new promise seems necessary.
Only one promise is necessary: when you create the never-resolved one. You can write that as
function debounce(func, wait) {
var timeout;
const never = new Promise(resolve => {/* do nothing*/});
return function() {
const result = timeout ? never : func.apply(this, arguments);
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
return result;
};
}
Or at least avoid the .then(resolve).catch(reject) part. Better write
function debounce(func, wait) {
var timeout;
return function() {
return new Promise(resolve => {
if (!timeout) resolve(func.apply(this, arguments));
// ^^^^^^^
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
});
};
}
And should you consider to reject the promise in case that the timeout has not yet occurred (so that the calling code can handle the rejection), you don't need new Promise either:
function debounce(func, wait) {
var timeout;
return function() {
const result = timeout
? Promise.reject(new Error("called during debounce period"))
: Promise.resolve(func.apply(this, arguments));
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait);
return result;
};
}
In essence, you need to share result of your debounce function. In your case, thats a promise:
const debouncedGetData = debounce(getData, 500)
let promiseCount = 0
let resultCount = 0
test()
function test() {
console.log('start')
callDebouncedThreeTimes()
setTimeout(callDebouncedThreeTimes, 200)
setTimeout(callDebouncedThreeTimes, 900)
}
function callDebouncedThreeTimes () {
for (let i=0; i<3; i++) {
debouncedGetData().then(r => {
console.log('Result count:', ++resultCount)
console.log('r', r)
})
}
}
function debounce(func, wait) {
let waiting;
let sharedResult;
return function() {
// first call will create the promise|value here
if (!waiting) {
setTimeout(clearWait, wait)
waiting = true
sharedResult = func.apply(this, arguments);
}
// else new calls within waitTime will be discarded but shared the result from first call
function clearWait() {
waiting = null
sharedResult = null
}
return sharedResult
};
}
function getData () {
console.log('Promise count:', ++promiseCount)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(666)
}, 1000)
})
}

How to simplify setTimeout in ReactJS

There was a typewriting animation in pure javaScript which has been converted to ReactJS. The setTimeout functions do not look clean and do not adhere to best practices according to ReactJS standard.
For example animationManager()
animationManager = () => {
this.rafRef = requestAnimationFrame(time => {
const typingData = this.props.data;
this.typeEffect(time, typingData[this.index], () => {
this.timeoutRef = setTimeout(() => {
this.rafRef = requestAnimationFrame(time => {
this.deleteEffect(time, () => {
this.timeoutRef = setTimeout(() => {
this.index =
this.index === typingData.length - 1 ? 0 : this.index + 1;
this.animationManager();
}, this.props.pauseBeforeRestarting);
});
});
}, this.props.pauseBeforeDeleting);
});
});
};
Is it possible to make it more clean with all these setTimout ?
Complete code 👉 https://codesandbox.io/s/qk4591q1kw
Yes, you can actually create functions that acts like a timer: it returns a promise that is resolved when the time runs out, something like this:
timer = (duration) => {
return new Promise(resolve => {
window.setTimeout(resolve, duration);
});
}
Similarly, you can do the same for requestAnimationFrame. The trick is to use ES6 spread operator so that you can pass arbitrary number of arguments into the callback to be invoked:
animationFrame = (callback, ...args) => {
return new Promise(resolve => {
window.requestAnimationFrame(time => {
callback(time, ...args);
});
})
}
Since you are using ES6, you can then use async functions to wait for the timer to complete, before moving on to execute the next line of code. If we break down your animationManager() code, it can be seen as following:
You want to start with typingEffect
Once typingEffect is completed, you want to trigger deleteEffect
In this case, we can refactor your code as such:
animationManager = () => {
const deleteFunc = (time, typingData) => {
this.deleteEffect(time, async () => {
await this.timer(this.props.pauseBeforeRestarting);
this.index = this.index === typingData.length - 1 ? 0 : this.index + 1;
this.animationManager();
});
};
const typeFunc = (time) => {
const typingData = this.props.data;
this.typeEffect(time, typingData[this.index], async () => {
await this.timer(this.props.pauseBeforeDeleting);
await this.animationFrame(deleteFunc, typingData);
})
};
this.animationFrame(typeFunc);
};
I have forked your example to provide a proof-of-concept of the slightly refactored code: https://codesandbox.io/s/308kxjzwrq
The common practice is to use Promises for that. You can create helper Promise which will use requestAnimationFrame, and make your flow flat and "thenable", by adding success callbacks onResolve.

Create a function that no matter how many times invoked run only when first async call finishes?

suppose I've a function fetch(id).
I would be calling it arbitrarily at random times.
But I want every successive call to run only after previous call has finished.
say the async task takes 4 seconds, and i call fetch 3 times. then total time should be 12 seconds.
I could make a array and at every call set promise to go through next in line.
but what are some ways to accomplish this.
I think I got it
//First my example function which could be anything but should return promise which would be queued
function example(n) {
return new Promise((res, rej) => {
setTimeout(()=> {
console.log(n);
res();
}, 1000);
});
}
//now solution
function debounce(func) {
let p = Promise.resolve();
return function(x){
p = p.then(() => func(x));
}
}
//usage
d = debounce(example);
d(1);d(2);d(3);d(4);d(5);d(6);d(1);
You can chain Promises without an array, just store a pointer to the last Promise
// async payload function
// returns a promise
function f(x) {
console.log(`call f(${x})`);
return new Promise((resolve) => {
setTimeout(() => {
console.log(`resolve f(${x})`);
resolve();
}, 2000);
});
}
// wrapper to call function `f` sequentially
// stores pointer to the last Promise in closure
const g = (function(){
let lastPromise = Promise.resolve();
return function(arg){
lastPromise = lastPromise.then(() => f(arg));
}
})();
// generate random calls of function function `g`
for (let i = 0; i < 5; i++) {
setTimeout(() => g(i), Math.random() * 100);
}
I guess you can use async.io library.
Or, each time you want to call your function, push the function itself in an array. When the previous function has finished, check if there is still some functions to call in the array.

Categories