美文网首页
你不知道的JavaScript 同步异步

你不知道的JavaScript 同步异步

作者: Nickyzhang | 来源:发表于2018-01-16 10:24 被阅读29次

    写在前面的话

    setTimeout是我们学习JavaScript基础都必须面对的问题,也许当时你搞懂了,但是过一段时间就又忘记了。最近事情不多,我将梳理出for + setTimeout相关的知识点,以及使用Promiseasync/await来加深对异步、同步的理解

    setTimeout

    直接进入正题:
    for (var i = 0; i < 5; i++) {
      setTimeout(function() {
        console.log(i);
      }, 1000);
    }
    

    也许我们希望输出的是 1,2,3,4,5,但实际情况是我们输出了5,5,5,5,5,这到底是为什么呢?
    由JS的运行机制中我们得知:当线程中没有任何同步代码的前提下才会执行异步代码。我们的for循环时同步的,但是setTimeout是异步的,由此就造成了输出5,5,5,5,5这种情况。

    通过一顿胡乱分析我们得出假设:只要保证里面的也变成同步的是不是就可以了呢?

    方式①

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

    这里使用的是最简单的方式:动态的改变延迟的时间。
    因为for循环时同步的,而setTimeout是异步的,所以会先把for循环执行完毕,然后再执行内部的setTimeout,所以输出结果为每隔1秒输出一个5

    方式②

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

    我们通过设置一个立即执行函数(IIFE),这样就能保证里面和外面的是同步执行的。

    方式③

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

    这里其实和第一种方法类似,只不过我们把这个函数单独拿出来,并把index值当做函数的参数来传递

    方式④

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

    letES6语法,for循环代码块构成一个作用域,里面的内容引用了上层作用域的变量 i,并最终形成五个闭包,而for使用var时,还是ES5的写法,for代码块没有形成作用域,所以里面的function不构成闭包。同理我们的方式①方式②都形成了闭包函数。

    如果我们需要最后一个延迟5秒,其余的都是延迟1秒,我们就可以用到ES6的语法--Promise,下面我们用Promise实现这一情况

    方式⑤

    const task = [];
    const output = (i) => new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log(i);
            resolve();
        }, i*1000)
    })
    for (var i = 0; i<5; i++){
        task.push(output(i));
    }
    Promise.all(task).then(() => {
        setTimeout(() => {
            console.log(i);
        }, 5000);
    })
    

    这里采用了Promise解决异步的方式,在ES7中解决异步还有async/await的方式

    方式⑥

    const sleep = (timeount) => new Promise((resolve) => {
        setTimeout(resolve, timeount);
    });
    
    Func = async () => {
        for(var i = 0; i<5; i++){
            await sleep(1000);
            console.log(i);
        }
        await sleep(5000);
        console.log(i);
    }
    
    this.Func();
    

    async 表示这是一个async函数,await只能用在这个函数里面;
    await 表示在这里等待promise返回结果了,再继续执行;
    await 后面跟着的应该是一个promise对象,否则没有同步效果

    相关文章

      网友评论

          本文标题:你不知道的JavaScript 同步异步

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