setTimeout是面試常見的考題,一直被虐,可是久病成良醫,總結一下我所瞭解的這兩個函數的特性和用法以及常見的地方面試
setTimeout(call,[wait,...args])
在wait
延時以後將call
函數加入執行隊列中,IE9+ 的瀏覽器能夠附加其餘額外參數做爲 call
的參數傳遞。只執行一次。setTnterval(call,[wait,...args])
,與setTimeout
相比,每間隔wait
以後就將call
加入執行隊列setTimeout
和setInterval
的返回值是一個正整數,主要爲了清除用的(恕我尚未在其餘地方使用過),清除對應的兩個函數clearTimeout
和clearInterval
,理論上setTimeout
和setInterval
共用一個計時器池,因此清除函數能夠混用,可是爲了代碼可讀仍是作區分。經過在內部封裝一個函數,而後在setTimeout
中遞歸的調用實現,美中不足的是沒法中止,也就是定時器id沒法獲取到。ajax
爲何要用setTimeout模擬setInterval?api
每一個setTimeout產生的任務會直接push到任務隊列中;而setInterval在每次把任務push到任務隊列前,都要進行一下判斷(看上次的任務是否仍在隊列中)。這樣某些間隔會被跳過promise
於是咱們通常用setTimeout模擬setInterval,來規避掉上面的缺點。瀏覽器
function func1(call, wait) {
var done = function() {
call()
setTimeout(() => {
done()
}, wait)
}
setTimeout(done, wait)
}
func1(() => {
console.log("hello")
}, 1000)
複製代碼
所謂閉包就是在函數A返回值是B函數,B函數使用了A函數的內部變量,B函數就造成了閉包,當A函數執行結束以後B函數中使用的A函數變量沒有跟着銷燬,還存在與內存中供B使用。閉包
常見考題app
可使用let
產生塊級做用域來處理異步
for (let i = 1; i <= 10; i++) {
setTimeout(() => {
console.log(i)
}, 1000 * i)
}
複製代碼
這個主要是理解程序的執行順序。程序執行分爲宏任務和微任務,setTimeout
和setInterval
產生的是宏任務會在下一次事件循環中執行。函數
setTimeout
等須要外部宿主(主要是瀏覽器)封裝的api執行的任務都會建立一個宏任務,在下一次也買你渲染以後執行。exampleui
console.log(1)
setTimeout(() => {
console.log(2)
}, 1000)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
console.log(5)
setTimeout(() => {
console.log(6)
})
// 1 3 5 4 6 2
複製代碼
setTimeout
交給瀏覽器計時器線程執行 等待 1秒以後將任務加入宏任務隊列promise
建立的時候的回調函數直接執行,打印3,.then
是微任務加入微任務隊列setTimeout
可是延時比以前的底,先加入宏任務隊列因爲setTimeout
中會產生一個宏任務,下一次執行的時候執行棧發生了改變,因此這裏的this很容易搞混。
這個理解和函數中的this一致,誰調用是誰。setTimeout返回執行的時候沒有對象調用它就是window,因此打印1。用箭頭函數能夠避免這個問題
var x = 1;
var obj = {
x: 2,
y: function(){
console.log(this.x);
}
};
setTimeout(obj.y,1000); // 1
複製代碼
只要是爲了屢次渲染頁面,可讓其餘任務穿插在每次也買你渲染之間執行,不至於頁面卡頓
要求:動態建立一個表格,一共10000行,每行10個單元格
/** * 常規的 */
var tbody = document.getElementsByTagName('tbody')[0];
var allLines = 10000;
// 每次渲染的行數
console.time('wd');
for(var i=0; i<allLines; i++){
var tr = document.createElement('tr');
for(var j=0; j<10; j++){
var td = document.createElement('td');
td.appendChild(document.createTextNode(i+','+j));
tr.appendChild(td);
}
tbody.appendChild(tr);
}
console.timeEnd('wd'); // 總共耗時180ms, 瀏覽器已經給出警告![Violation] 'setTimeout' handler took 53ms
/** * setTimeout 切割 */
var tbody = document.getElementsByTagName('tbody')[0];
var allLines = 10000;
// 每次渲染的行數
var everyTimeCreateLines = 80;
// 當前行
var currentLine = 0;
setTimeout(function renderTable(){
console.time('wd');
for(var i=currentLine; i<currentLine+everyTimeCreateLines && i<allLines; i++){
var tr = document.createElement('tr');
for(var j=0; j<10; j++){
var td = document.createElement('td');
td.appendChild(document.createTextNode(i+','+j));
tr.appendChild(td);
}
tbody.appendChild(tr);
}
console.timeEnd('wd');
currentLine = i;
if(currentLine < allLines){
setTimeout(renderTable,0);
}
},0);
// 此次異步按批次建立,沒有耗時的警告。由於控制了每次代碼在50ms內運行。實際上每80行耗時約10ms左右。這就不會引發頁面卡頓等問題。
複製代碼