浏览器如何异步执行Javascript并进行渲染

4

这里是在jsfiddle上的代码

<script>
  function updateSync1() {
    for (var i = 0; i < 1000; i++) {
      document.getElementById('output').innerHTML = i;
    }
  }

  function updateSync2() {
    for (var i = 0; i < 1000; i++) {
      setTimeout(document.getElementById('output').innerHTML = i, 0);
    }
  }

  function updateAsync() {
    var i = 0;

    function updateLater() {
      document.getElementById('output').innerHTML = (i++);
      if (i < 1000) {
        setTimeout(updateLater, 0);
      }
    }

    updateLater();
  }
</script>

<div class="row btn_area">
  <button class="btn btn-info" onclick="updateSync1()">Run Sync 1</button>
  <button class="btn btn-info" onclick="updateSync2()">Run Sync 2</button>
  <button class="btn btn-info" onclick="updateAsync()">Run Async</button>
  <span class="label label-info pull-right" style="display:block;" id="output"></span>
</div>

http://jsfiddle.net/himaneasy/y1534ths/

当我点击“运行同步1”时,代码将直接运行到999。
当我点击“运行同步2”时,代码将直接运行到999。
当我点击“运行异步”时,页面将逐一呈现。
有人可以帮忙解释一下“运行同步1”和“运行同步2”的区别吗?为什么在“运行同步2”中使用setTimeout不能使它逐一呈现?
2个回答

8
Javascript执行是单线程的。它使用任务队列和堆栈来执行代码。 这段代码:
for (var i=0;i<length;i++) {
     setTimeout(drawChartFunc,0);
}

将在任务队列中添加[length]个setTimeouts调用,并依次执行它们(0毫秒超时)。只有最后一个操作会更新屏幕,因为所有超时任务都在循环之后的堆栈上(循环之后,任务队列包含[length]个setTimeout调用)。每个超时都执行drawChartFunc。现在,drawChartFunc确实在任务队列中放置了一个屏幕更新函数,但剩余的超时先执行,因此首先执行下一个超时 - 屏幕更新函数只能在[length]个setTimeout调用完成后(从任务队列/堆栈中取出)执行。这也是依次完成的,但速度非常快。如果您的眼睛训练有素,可以在输出中看到随后的数字;)

现在

function updateLater() {
     drawChartFunc();
     i++;
     if (i < length) { 
         setTimeout(updateLater, 0);
     }
 }

首先运行drawChartFunc,将屏幕更新放入任务队列中,然后将增量i放入任务队列中,并在适当情况下在此之后向任务队列添加新的setTimeout。换句话说,drawChartFunc被放入堆栈中,它将屏幕更新放入堆栈中,两者都被执行,然后将超时放入堆栈,再将drawChartFunc放入堆栈...等等。
关于JavaScript任务队列/堆栈:这个视频对我非常有用。
这里是您的jsFiddle,稍作修改。它展示了两种方法的排队过程。

0

setTimeout(callback, interval)函数接收两个参数:回调函数和执行时间间隔。其中的interval参数决定了回调函数执行的时间:在你的情况下,是0毫秒,也就是尽快执行。 在你的代码中:

 function updateSync2() {
    for (var i = 0; i < 1000; i++) {
      setTimeout(document.getElementById('output').innerHTML = i, 0);
    }
  }

你已经创建了1000个setTimeout函数,它们都尽可能快地执行。


setTimeout仍然是异步的,这意味着“尽快”。 - Bergi
@Bergi 谢谢,是的,那是一个更好的表达方式。 - kmoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接