美文网首页bb7bb的前端书屋让前端飞程序员
关于setTimeout()的异步性与其同步处理

关于setTimeout()的异步性与其同步处理

作者: bb7bb | 来源:发表于2018-03-14 15:46 被阅读173次

    setTimeout()

    我们先从这个函数说起,setTimeout()- 暂停指定的毫秒数后执行指定的代码。

    setTimeout(fn,ms);其中共有两个参数,其中fn表示要执行的代码,可以是一个包含javascript代码的字符串,也可以是一个函数。第二个参数ms是以毫秒表示的时间,表示fn需推迟多长时间执行。clearTimeout()方法用于停止执行setTimeout()方法的函数代码。

    let timer = setTimeout(function() {
        console.log("50ms后输出");
    }, 50);
    //50ms后输出
    

    上面这个函数表示的是在50毫秒后执行console.log("50ms后输出");

    那么,问题来了,到底js是怎么处理setTimeout函数的?这50ms之间又能发生什么?其实这要涉及到js的异步机制,setTimeout就是一个异步处理的函数。那什么是异步处理呢?我们用另外一个函数来给大家演示一下:

    (function () {
        let timer = setTimeout(function() {
            console.log("0");
        }, 0);
        console.log("1")
    })();
    

    我们把间隔时间设置0,让他可以达到直接输出的效果,但是结果又是怎样呢?显然结果与我们想象的有些不一样,输出结果如下:

    1
    0
    

    这就很奇怪了,明明应该先输出0的,输出的顺序却发生了改变。这就是因为setTimeout是一个异步处理的函数。

    因为js是单线程,所以js异步机制是,当js代码中碰到异步操作时,会先跳过该操作,先执行下面的代码,在异步操作完成后再执行。这样就很容易明白上面的输出顺序了。js碰到setTimeout()后尽管其间隔为0秒,还是会跳过该函数先往下执行,这样就会输出1后再输出0。从setTimeout()角度看,该函数会被暂时挂起,等待时间完成后再加入到js的任务队列末中。所以我们就得到了上面的结果

    那么是往下的代码全部执行完再回头执行该异步操作吗?我们可以用一个简单的函数作为实例,答案告诉我们这是对的,在其所在的作用域内,等其中词法环境中所有的代码都执行完毕后再回头执行该异步操作,原因是其达到设置时间后,被加入到了任务队列的末尾,因此在最后才执行该函数

    (function () {
        let timer = setTimeout(function() {
            console.log("0");
        }, 0);
        for (let i = 1; i < 100; i++) {
            console.log(i);
        }
        (function () {
            for (let i = 1; i < 11; i++) {
                console.log("1");
            }
        })();
    })();
    //0 1 2 3 .... 98 99 先完成循环中输出1-99
    //1 1 1 1 1 1 1 1 1 1 再输出10个1
    //0 最后输出0
    

    ok,相信大家对异步函数也有一点儿了解了。那么重点来了,setTimeout()既然是异步函数,那么如果想要实现这么一个想法:间隔3s后再间隔2s执行某个操作,应该怎么完成?或者说要同步处理连续性的setTimeout()要怎么解决?
    我们先来看看这样的代码能不能实现

    (function () {
        let timer1 = setTimeout(function() {
            console.log("0");
        }, 3000);
        let timer2 = setTimeout(function() {
            console.log("1");
        }, 2000);
        
    })();
    
    //2s后输出1
    //3s后输出0
    

    显然,这是不可能实现的,因为setTimeout()开始执行时会被挂起,然后等到时间结束后排到任务队列末。代码开始运行时,timer1会被挂起,代码继续往下执行,遇到timer2也被挂起。当然,timer2间隔时间为2s,比timer1时间短,因此timer2将会先加入到任务队列末,所以先执行timer2输出1。随后再过1stimer1也到时间后再加入任务队列末,所以我们不难想到,按照这个按顺序执行下来。这整个函数的完成时间是3s,而不是我们想要的5s
    这个时候我介绍两种方法给大家解决:

    1.使用回调函数
    function time1(callback) {
        let timer1 = setTimeout(function() {
            console.log("0");
            callback();
        }, 3000);
        
        
    }
    time1(function () {
        let timer2 = setTimeout(function() {
            console.log("1");
        }, 2000);
    });
    //0 3s后输出
    //2 5s后输出
    
    1方法变形,嵌套函数
    function time1(callback) {
        let timer1 = setTimeout(function() {
            console.log("0");
            let timer2 = setTimeout(function() {    //直接将第二个timeout函数嵌套到第一个里面去
                console.log("1");
            }, 2000);
        }, 3000);
    }
    //0 3s后输出
    //2 5s后输出
    
    2.使用递归函数
    function time1() {
        let timer1 = setTimeout(function() {
            console.log("1");
            time1();
        }, 3000);
    }
    time1();
    //1 每隔3s输出
    

    第二种方法完成了setInterval()的功能,实际上setInterval()也是与setTimeout()一样的机制。

    那么到这里为止,我们就已经可以同步的去处理setTimeout()函数了,达到我们想要的目的。当然处理异步方法还有迭代函数,不过在这里却不适用于setTimeout(),因为他是按时间决定顺序。

    最后,如果该文章有什么错误遗漏的地方欢迎指出,我会及时更正。大家在学习的同时我也在学习,谢谢大家。

    相关文章

      网友评论

      • b12fe49e9ff0:文中有2个地方有点描述问题吧,第一个在“当js代码中碰到异步操作时,会先跳过该操作,先执行下面的代码,在异步操作完成后再执行”,这里应该是在同步操作完成后再执行。第二个是执行代码结果那里应该是输出1,并没有2的输出。
        bb7bb:@Linkin_JS 哦哦,确实不严谨,我先把它删了。到时要修改

      本文标题:关于setTimeout()的异步性与其同步处理

      本文链接:https://www.haomeiwen.com/subject/cadfqftx.html