title | comments | date | type | categories | tags | ||||
---|---|---|---|---|---|---|---|---|---|
setTimeout vs setInterval |
true |
2018-10-31 13:23:26 -0700 |
categories |
Full stack |
|
http://www.laruence.com/2009/09/23/1089.html
https://www.jeffjade.com/2015/08/03/2015-08-03-javascript-this/
https://www.youtube.com/watch?v=8aGhZQkoFbQ
https://www.zhihu.com/question/55050034
https://codepen.io/discountry/pen/XRXdVX?editors=1011
-
After
delay
period, putcb
to taskQueue of main thread, rather than executingcb
right away!- only guarantees after exact
delay
,cb
is scheduled, - cannot guarantee
cb
being executed after exactdelay
, because maybe at that time, the main thread is still dealing a time-consuming task, which will postpone the execution ofcb
- only guarantees after exact
-
Good use case: recursively schedule delayed task, like a
node
data structure- deal with CPU intensive task, set aside period for UI update to be executed to avoid freezing
- eg. break down large task into smaller ones, schedule smaller task execution every delay period
- the bigger
delay
period is, the longer time left for UI - the time left for UI is exactly
delay
period - NOTE: don't use
setInterval
, since the execution time forcb
can be longer thandelay
, resulting in there's no time left for UI update. Whencb
finishes execution, the UI update and nextcb
will race to be executed in next event loop
- the bigger
-
**
setTimeout(cb, 0):
**-
- 等到Call Stack为空,即下一轮 Event Loop的时候 执行
cb
- 等到Call Stack为空,即下一轮 Event Loop的时候 执行
-
- Render Queue 比 Callback Queue 在browser中的优先级高,他们共用一个JS V8 Engine。因此,callback中的
cb
会等到DOM被render之后在调用,这就是为什么使用 **setTimeout(cb, 0)
**
- Render Queue 比 Callback Queue 在browser中的优先级高,他们共用一个JS V8 Engine。因此,callback中的
-
- 这样,即便有很多
setTimeout(cb, 0)
短时间大量被调用,也会因为**render queue的高优先级,使得它能够在两个cb
调用之间,插入到 JS V8 thread中update DOM,从而使页面不会freeze**
- 这样,即便有很多
-
Waiting for elements mounted to DOM by GUI thread, then switch to JS thread, do the manipulation by
cb
-
Every function put inside
setTimeout
with0
delay, will respect all the DOM task, and will scheduled after all DOM tasck -
will execute right after all synchronous task is finished on stack by main thread
-
i.e. has to wait for next event loop come
-
Great Use Case:
-
set the order of event handler:
-
// 1. parent event_handler will be executed first, // 2. then the target element let input = document.getElementById('myButton'); input.onclick = function A() { setTimeout(function B() { input.value +=' input'; }, 0) }; document.body.onclick = function C() { input.value += ' body' };
-
-
tackle the race problem of keypress
-
/* keypress has two results: 1. give input the value of pressed key [asynchronous - hardware] 2. call the keypress event handler [asynchronous - event] BAD version: - cannot get the current key value when callback is called - can only get the value of previous pressed key value GOOD version: - use setTimeout(fn, 0) - callback will be executed after the key value is updated in DOM */ <input type="text" id="input-box"> // 1. BAD Version document.getElementById('input-box').onkeypress = function (event) { this.value = this.value.toUpperCase(); } // 2. GOOD Version document.getElementById('input-box').onkeypress = function() { let self = this; setTimeout(function() { self.value = self.value.toUpperCase(); }, 0); }
-
-
-
-
Every
delay
period, putcb
to taskQueue of main thread if there is nocb
waiting to be executed on the taskQueue; Otherwise, won't schedulecb
evendelay
period is achieved -
Note:
- when delay is up, even there exists an CPU-intensive task ,
setInterval
won't wait for the finish of the current task, instead put thecb
directly to taskQueue at the exact time spot; - But if the main thread is still executing task, the
cb
has to waits to be executed - There is NO accumulated
cb
scheduled on event queue, even if there is a CPU intensive task blocking the periodiccb
scheduling. setInterval
timer has a check mechnism to ensure, there will be only onecb
on event queue
- when delay is up, even there exists an CPU-intensive task ,
-
Common use case: CSS animation
-
Note1:
- it only focus scheduling
cb
afterdelay
, don't care how long vacant time left - it's possible that execution of
cb
is longer thandelay
, then nextcb
will be executed right after firstcb
finishes
- it only focus scheduling
-
Note2:
-
mustn't use time variable as terminating condition to clear interval,
-
because some time it cost longer to execute
cb
, making some scheduled task won't be executed -
use other variable as terminating condition to clear interval
-
let div = document.getElementById('someDiv'); let opacity = 1; let fader = setInterval(function() { opacity -= 0.1; if (opacity >= 0) { div.style.opacity = opacity; } else { clearInterval(fader); // use `opacity` as terminating condition } }, 100);
-
-
-
this
problem => because ofwindow.setTimeout
orwindow.setInterval
-
setTimeout( function(){ this.value=50 }, 1000 )
-
Problem:
- example above, exist
this
in the callback function, if notbind
actual object,this
will referwindow
setTimeout
setInterval
will change the reference ofthis
towindow
- example above, exist
-
Why?
this
always refers to the object who calls the function wherethis
resides- when
cb
executed, it is executed likecb()
, which omits the prefix object:window
-
How to solve?
-
use
bind()
to produce a newcb
function carrying this reference to right object -
use
apply
orcall
-
a = "string belongs to window" // note: not " let a = ... "; obj = { a: "string belongs to obj", printA: function(){ console.log(this.a) } } // 1. "string belongs to window", after 1s setTimeout(obj.printA, 1000) // 2. "string belongs to window", after 1s let temp = obj.printA setTimeout(temp, 1000) // 3. "string belongs to obj" setTimeout(function(){ obj.printA.apply(obj) }, 2000) // 4. "string belongs to obj" setTimeout(function(){ obj.printA() }, 3000)
-
-
-
Implement a
setInterval
usingsetTimeout
-
function customSetInterval( cb, delay ){ let time_handle = null function wrapper(){ cb.apply(null) time_handle= setTimeout(wrapper, delay) } time_handle= setTimeout(wrapper, delay) return time_handle } // Test let i = 0 function cb(){ i+=1; console.log(i) } let interval_handle = customSetInterval(cb, 1000) // print 1 , 2 , 3 , 4 , ... // clearTimeout(interval_handle) // use to clear interval
-
-
how to handle encoding task for a file (CPU intensive)?
-
Problem: it might freeze the UI when encode the whole file
-
Solution:
-
Divide the file into several part, using a symbol, or using size
-
for each small part, just take over CPU, since it's small task
-
between each encoding, leave time for UI interaction
-
Note: use
setTimeout
instead ofsetInterval
-
Since the execution time for
cb
can be longer thandelay
, resulting in there's no time left for UI update. Whencb
finishes execution, the UI update and nextcb
will race to be executed in next event loop -
/* input: the file to be encoded output: the encoded goal file totalSize: the size of the input file encode: the function used to encode */ function encodeWrapper( input, output, totalSize, encode ){ let output = "" let accumSize = 0 let i = 0 let timeoutHandle = null /* delayed recursion i: ith step stepSize: how big to encod for a step */ function encodeHelper(i, stepSize ){ output += encode( input.substring(i*stepSize,stepSize) ) if( i*stepSize > totalSize ) { clearInterval(timeoutHandle) return output } timeoutHandle = setTimeout(encodeHelper(i+1, stepSize), 100) } return encodeHelper(0, 1024) }
-
-
-
How to write a delayed recursion ?
-
Problem: Calculate the sum from 0 to n
-
Solution: f(n) = f(n-1) + n; f(0) = 0
-
/* f(n) = f(n-1) + n f(0) = 0 */ function wrapper(n){ let timeHandle = 0 let accum = 0 // recursive call itself every 1000ms function sum(n){ timeHandle=setTimeout(function(){ if (n === 0) { clearTimeout(timeHandle) alert(accum) // output the result return } accum += n sum(n-1) },1000) } sum(n) }
-
-
Debouncify a function
-
Problem: Some Ajax functions can be frequently called using events, which will cause severe performance problem on backend
-
Solution:
- we want to call Ajax function only after a period delay time (so that we can make sure the user is really waiting to for to make Ajax call, rather than in the middle of typing)
- when there is another intention to call the Ajax function in the middle of last delay, we want to reschedule the ajax call after another delay
-
function callAjaxRightAway(){ console.log("Ajax call starting, with arguments: " + [...arguments] ) } function Debouncify( fn, delay ){ let time_handle = null return function(){ clearTimeout(time_handle) let args = arguments time_handle = setTimeout( function(){ fn.apply(null,args) }, delay) } } let delayedFun = Debouncify(callAjaxRightAway, 2000) delayedFun({arr:[1, 2, 3], heigh: 10 }) // will be ignored delayedFun(1,2) // will be ignored delayedFun("a", "b", "c") // "Ajax call starting, with arguments: a,b,c"
-