浏览器事件循环--宏任务和微任务

JS为什么是单线程的?

浏览器js的作用是操作DOM,这决定了它只能是单线程的,不然会带来复杂的同步问题。假设js同时有两个线程,一个线程在某个dom上添加内容,另一个线程删除了这个dom节点,这时浏览器该以哪个线程为准?

任务队列

单线程就意味着所有的任务需要排队,但是I/O操作时cpu是闲着的。所以js就设计成了一门异步的语言。

任务分为两种:一种是同步任务(synchronous),另一种是异步任务(asynchronous)

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
  • 主线程外,还维护着一个**任务队列**。只要异步任务有了运行结果,就在任务队列之中放置一个事件
  • 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务就会结束等待状态,进入执行栈,开始执行
  •  主线程不断重复上面的第三步

例如下列代码:

let fn = () => {

  console.log('fn');

}

setTimeout(fn, 1000)

console.log('main');



//main

//fn

  • setTimeout先进入主线程,然后异步fn函数被加入到I/O中
  •  setTimeout出栈,主线程继续执行,console进入主线程,打印`main`
  •  1秒之后将fn加入到任务队列
  • 任务队列检查主线程是否有任务在执行,发现主线程没有任务,将fn函数加入到主线程,打印`fn`

主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Lop(事件循环)

宏任务与微任务

除了广义的同步任务和异步任务,js单线程中的任务可以细分为宏任务(macrotask)和微任务(microtask)

  • 宏任务:script(整体代码)、`setTimeout`、`setInterval`、`setImmediate`、`I/O`、`UI rendering`
  •  微任务:`Promise`、`process`、`nextTick`、`Object`、`observe`、`MutationObserver`

  • 宏任务进入主线程,执行过程中会收集微任务加入微任务队列
  • 宏任务执行完成后,立马执行微任务中的任务。微任务执行过程中将再次收集宏任务,并加入到宏任务队列
  • 反复执行1,2操作

浏览器事件循环--宏任务和微任务_第1张图片

例如下列代码

setTimeout(() => {

  console.log('setTimeout');

})


Promise.resolve().then(() => {

  console.log('promise');

})


console.log('main');



//main

//promise

//setTimeout
  •  首先setTimeout进入宏任务,然后将`log('setTimeout')`放到I/O中,setTimeout出队
  •  然后Promise中的`log('promise')`加入到微任务队列中
  •  `log('main')`加入到宏任务队列,打印`main`,`log('main')`出队
  •  宏任务执行完毕,执行微任务,打印`promise`
  •  微任务执行完毕,没有需要执行的新的宏任务。**此处就是事件循环的一环事件(每执行完一轮宏任务和一轮微任务)**
  •  将`log('setTimeout')`加入到宏任务,打印`setTimeout`

最后

在我的博客网站上有更多我的一些学习成果

你可能感兴趣的:(Node,javascript,前端,开发语言)