Open Twlig opened 2 years ago
javascript是一门单线程的非阻塞的脚本语言。这是由其最初的用途来决定的:与浏览器交互。
单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。
非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。
JS是单线程与它的用途有关,作为浏览器脚本,JS主要用途是与用户互动,以及操作DOM,这决定了JS只能是单线程的。假定JS同时有两个线程,一个改变某个DOM节点的内容,另一个删除这个DOM节点,则会发生冲突和错误。
非阻塞则是,为了防止一直处于等待状态,影响用户体验。通过将异步任务放入队列中,可以先执行同步任务。
而“非阻塞”,javascript引擎是如何实现的这一点呢?答案就是——event loop(事件循环)
当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。执行栈中存放方法的执行上下文,执行方法时从栈顶执行,执行结束弹出该方法的执行上下文,并继续执行下一个栈顶方法。
同步代码可以按照顺序直接依次加入执行栈,而对于异步代码则不能直接加入执行栈,需要用到事件队列。
js引擎遇到一个异步事件后会将事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将事件加入事件队列。
加入事件队列的回调不会被立刻执行:
等待主线程处于闲置状态
事件被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。
执行事件队列中的任务对应的回调
异步任务也有不同,执行优先级也有区别。不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。
以下事件属于宏任务:
setInterval()
setTimeout()
以下事件属于微任务
new Promise()
new MutaionObserver()
首先,整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为同步任务、异步任务两部分
同步任务会直接进入主线程依次执行
异步任务会再分为宏任务和微任务
宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列中
微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列中
当主线程内的任务执行完毕,主线程为空时,会检查微任务的事件队列,如果有任务,就全部执行,如果没有就执行下一个宏任务(不会执行全部宏任务)
上述过程会不断重复,这就是Event Loop,比较完整的事件循环
单线程和非阻塞
javascript是一门单线程的非阻塞的脚本语言。这是由其最初的用途来决定的:与浏览器交互。
单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。
非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。
JS是单线程与它的用途有关,作为浏览器脚本,JS主要用途是与用户互动,以及操作DOM,这决定了JS只能是单线程的。假定JS同时有两个线程,一个改变某个DOM节点的内容,另一个删除这个DOM节点,则会发生冲突和错误。
非阻塞则是,为了防止一直处于等待状态,影响用户体验。通过将异步任务放入队列中,可以先执行同步任务。
而“非阻塞”,javascript引擎是如何实现的这一点呢?答案就是——event loop(事件循环)
执行栈和事件队列
当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。执行栈中存放方法的执行上下文,执行方法时从栈顶执行,执行结束弹出该方法的执行上下文,并继续执行下一个栈顶方法。
同步代码可以按照顺序直接依次加入执行栈,而对于异步代码则不能直接加入执行栈,需要用到事件队列。
js引擎遇到一个异步事件后会将事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将事件加入事件队列。
加入事件队列的回调不会被立刻执行:
等待主线程处于闲置状态
事件被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。
执行事件队列中的任务对应的回调
宏任务和微任务
异步任务也有不同,执行优先级也有区别。不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。
以下事件属于宏任务:
setInterval()
setTimeout()
以下事件属于微任务
new Promise()
new MutaionObserver()
事件循环过程
首先,整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为同步任务、异步任务两部分
同步任务会直接进入主线程依次执行
异步任务会再分为宏任务和微任务
宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列中
微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到事件队列中
当主线程内的任务执行完毕,主线程为空时,会检查微任务的事件队列,如果有任务,就全部执行,如果没有就执行下一个宏任务(不会执行全部宏任务)
上述过程会不断重复,这就是Event Loop,比较完整的事件循环