JS执行机制 - 图1

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。 这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

同步与异步

JS执行机制 - 图2

同步

  • 同步行为对应内存中顺序执行的处理器指令
  • 每条指令都会严格按照它们出现的顺序来执行,而每条指令执行后也能立即获得存储在系统本地(如寄存器或系统内存)的信息。
  • 这样的执行流程容易分析程序在执行到代码任意位置时的状态(比如变量的值)。
  • 同步操作的例子可以是执行一次简单的数学计算:
    1. let sun = 10;
    2. sun+=5
    在程序执行的每一步,都可以推断出程序的状态。这是因为后面的指令总是在前面的指令完成后才会执行。等到最后一条指令执行完毕,存储在 x 的值就立即可以使用。这两行 JavaScript 代码对应的低级指令(从 JavaScript 到 x86)并不难想象。
    首先,操作系统会在栈内存上分配一个存储浮点数值的空间,然后针对这个值做一个数学计算,再把计算结果写回之前分配的内存中。所有这些指令都是在单个线程中按顺序执行的。在低级指令的层面,有充足的工具可以确定系统状态。

异步

  • 相对的,异步行为类似于系统中断,即当前进程外部的实体可以触发代码执行。
  • 异步操作经常是必要的,因为强制等待一个长时间的操作通常是不可行的(同步操作则必须要等)
  • 如果代码要访问一些高延迟的资源,比如向原创服务器发送请求并等待响应,那么就会出现长时间的等待。
  • 异步操作的例子可以是在定时回调中执行一次简单的数学计算:
    1. let x = 3;
    2. setTimeout(() => x = x + 4, 1000);
    这段程序最终与同步代码执行的任务一样,都是把两个数加在一起,但这一次执行线程不知道 x 值
    何时会改变,因为这取决于回调何时从消息队列出列并执行。

异步代码不容易推断。虽然这个例子对应的低级代码最终跟前面的例子没什么区别,但第二个指令
块(加操作及赋值操作)是由系统计时器触发的,这会生成一个入队执行的中断。到底什么时候会触发
这个中断,实际上无法预知。无论如何,在排定回调以后基本没办法知道系统状态何时变化。

为了让后续代码能够使用 x,异步执行的函数需要在更新 x 的值以后通知其他代码。如果程序不需
要这个值,那么就只管继续执行,不必等待这个结果了。

2.JS执行机制(事件循环)

1551435335464.png