JS是单线程的,或者说只有一个主线程,也就是它一次只能执行一段代码。JS中实际上是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS的设计初衷就没有考虑这些,针对JS这种不具有并行任务处理的特性,咱们称之为“单线程”。javascript
虽然JS运行在浏览器中是单线程的,可是浏览器是事件驱动的(Event driven),浏览器中不少行为是异步(Asynchronized)的,会建立事件并放入执行队列中。浏览器中不少异步行为都是由浏览器新开一个线程去完成,一个浏览器至少实现三个常驻线程:java
JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,通常会附带在网页浏览器之中,好比最出名的就是Chrome浏览器的V8引擎,以下图所示,JS引擎主要有两个组件构成:ajax
每个函数执行的时候,都会生成新的execution context(执行上下文),执行上下文会包含一些当前函数的参数、局部变量之类的信息,它会被推入栈中, running execution context(正在执行的上下文)始终处于栈的顶部。当函数执行完后,它的执行上下文会从栈弹出。 数据库
function bar() {
console.log('bar');
}
function foo() {
console.log('foo');
bar();
}
foo();
复制代码
执行过程当中栈的变化: promise
Wikipedia这样定义:浏览器
"Event Loop是一个程序结构,用于等待和发送消息和事件。(a programming construct that waits for and dispatches events or messages in a program.)"bash
简单说,就是在程序中设置两个线程:一个负责程序自己的运行,称为"主线程";另外一个负责主线程与其余进程(主要是各类I/O操做)的通讯,被称为"Event Loop线程"(能够译为"消息线程")。 多线程
事件循环能够简单描述为:并发
接下来看一个异步函数执行的例子:异步
var start=new Date();
setTimeout(function cb(){
console.log("时间间隔:",new Date()-start+'ms');
},500);
while(new Date()-start<1000){};
复制代码
其实咱们上面所说的都是宏任务(Macrotasks),可是js中还有一种队列微任务(Microtasks)。
macro-task(Task):一个event loop有一个或者多个task队列。task任务源很是宽泛,好比ajax的onload,click事件,基本上咱们常常绑定的各类事件都是task任务源,还有数据库操做(IndexedDB ),须要注意的是setTimeout、setInterval、setImmediate也是task任务源。总结来讲task任务源:
micro-task(Job):microtask 队列和task 队列有些类似,都是先进先出的队列,由指定的任务源去提供任务,不一样的是一个 event loop里只有一个microtask 队列。另外microtask执行时机和Macrotasks也有所差别
那么Macrotasks和Microtasks有什么别区别呢
举个简单的例子,假设一个script标签的代码以下:
Promise.resolve().then(function promise1 () {
console.log('promise1');
})
setTimeout(function setTimeout1 (){
console.log('setTimeout1')
Promise.resolve().then(function promise2 () {
console.log('promise2');
})
}, 0)
setTimeout(function setTimeout2 (){
console.log('setTimeout2')
}, 0)
复制代码
运行过程:
script里的代码被列为一个task,放入task队列。
*【task队列:setTimeout1 setTimeout2;microtask队列:】 4. 从task队列中取出setTimeout1,推入栈中执行,将promise2列为microtask。
综上所说,每次event loop循环执行栈完成后,会继续执行完相应的microtask任务
这是event loop中很重要部分,在第7步会进行Update the rendering(更新渲染),规范容许浏览器本身选择是否更新视图。也就是说可能不是每轮事件循环都去更新视图,只在有必要的时候才更新视图。