JavaScript学习笔记【setTimeout】

566 阅读1分钟

JavaScript学习笔记【setTimeout】

先看两个例子

console.log(1);
setTimeout(function () {
    console.log(2);
}, 0);
console.log(3);

上面代码输出结果是1 3 2

无论setTimeout的执行时间是0还是1000,结果都是先输出3后输出2

for (var i = 0; i < 4; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

输出结果为 4 4 4 4

一、JavaScript单线程

首先我们要知道JavaScript 是单线程的。JS在同一时间内只能做一件事,这也常被称为 “阻塞式执行”。

可以采用异步来实现非堵塞执行

任务队列:一个先进先出的队列,它里面存放着各种事件和任务。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

  • 输出
  • 变量的声明
  • 同步函数

异步任务

  • setTimeout和setInterval
  • DOM事件
  • Promise
  • process.nextTick
  • fs.readFile
  • http.get
  • 异步函数 除此之外,任务队列又分为macro-task(宏任务)与micro-task(微任务),在ES5标准中,它们被分别称为task与job。

二、setTimeout运行机制

setTimeout是异步的,运行机制是指定的代码,必须等到本次执行的所有同步代码都执行完,才会执行。

优先关系:异步任务要挂起,先执行同步任务,同步任务执行完毕才会响应异步任务。

这里解释下异步执行过程:

浏览器有个定时器(timer)模块,定时器到了执行时间才会把异步任务放到异步队列,for循环体执行的过程中并没有把setTimeout放到异步队列中,只是交给定时器模块了。

4个循环体执行速度非常快(不到1毫秒)。定时器(即使设置了0默认也是4毫秒)到了设置的时间才会把setTimeout语句放到异步队列中。

图片来源叫我汤先森

三、解决方法

对于上面第二个例子想要得到0 1 2 3的结果可以使用以下方法。

方法一:使用let

使用let,不要用var,因为let是有作用域的,所以setTimeout的值指向的是每个循环体中的值

for (let i = 0; i < 4; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

方法二:使用闭包

for (var i = 0; i < 4; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        }, 1000 * i)
    })(i);
}

因为每个i的值都会传入function中,setTimeout中的i作用域在这个闭包中

基于Promise的解决方案(暂未理解)

const tasks = [];

const output = (i) => new Promise((resolve) => {
    setTimeout(() => {
        console.log(i);
        resolve();
    }, 1000 * i);

});

//生成全部的异步操作
for (var i = 0; i < 5; i++) {
    tasks.push(output(i));
}
//同步操作完成后,输出最后的i
Promise.all(tasks).then(() => {
    setTimeout(() => {
        console.log(i);
    }, 1000)
})

使用ES7中的async await特性的解决方案(暂未理解)

const sleep = (timeountMS) => new Promise((resolve) => {
    setTimeout(resolve, timeountMS);
});

(async () => { //声明即执行的async
    for (var i = 0; i < 5; i++) {
        await sleep(1000);
        console.log(i);
    }

    await sleep(1000);
    console.log(i);

})();

参考文献: www.cnblogs.com/tangjianqia… www.jianshu.com/p/3facc9bd8…