setTimeout

setTimeout是面试常见的考题,一直被虐,可是久病成良医,总结一下我所了解的这两个函数的特性和用法以及常见的地方面试

基础用法

  • setTimeout(call,[wait,...args])wait 延时以后将call 函数加入执行队列中,IE9+ 的浏览器能够附加其余额外参数做为 call 的参数传递。只执行一次。
  • setTnterval(call,[wait,...args]) ,与setTimeout相比,每间隔wait以后就将call加入执行队列
  • setTimeoutsetInterval的返回值是一个正整数,主要为了清除用的(恕我尚未在其余地方使用过),清除对应的两个函数clearTimeoutclearInterval,理论上setTimeoutsetInterval共用一个计时器池,因此清除函数能够混用,可是为了代码可读仍是作区分。

用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)
复制代码

间隔为一秒连续打印1~10

所谓闭包就是在函数A返回值是B函数,B函数使用了A函数的内部变量,B函数就造成了闭包,当A函数执行结束以后B函数中使用的A函数变量没有跟着销毁,还存在与内存中供B使用。闭包

常见考题app

可使用let 产生块级做用域来处理异步

for (let i = 1; i <= 10; i++) {
    setTimeout(() => {
      console.log(i)
    }, 1000 * i)
  }
复制代码

事件循环

这个主要是理解程序的执行顺序。程序执行分为宏任务和微任务,setTimeoutsetInterval产生的是宏任务会在下一次事件循环中执行。函数

  • 首先执行主线程,遇到ajax网路请求、用户与浏览器交互事件(点击,滚动等)、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
复制代码
  • 主线程开始执行,打印1
  • 遇到setTimeout交给浏览器计时器线程执行 等待 1秒以后将任务加入宏任务队列
  • promise建立的时候的回调函数直接执行,打印3,.then是微任务加入微任务队列
  • 打印 5
  • 又是setTimeout 可是延时比以前的底,先加入宏任务队列
  • 第一个宏任务完成,执行微任务,只有一个打印4
  • 而后挨个执行宏任务队列 打印 6 和 2

setTimeout 中的this

因为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左右。这就不会引发页面卡顿等问题。
复制代码
相关文章
相关标签/搜索