你不知道的JavaScript 同步异步

2018-01-16  本文已影响29人  Nickyzhang

写在前面的话

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对象,否则没有同步效果

上一篇 下一篇

猜你喜欢

热点阅读