单线程
众所周知,JS是单线程的语言,之所以是单线程,用一句烂大街的话就是,如果两个线程同时操作一个DOM节点,那么该以哪个为准呢,虽然多线程也有办法解决,但是js毕竟是浏览器脚本语言,不需要那么复杂
但是单线程遇到多个任务,需要排队执行,如果遇到定时器任务或者ajax请求等等,那会严重影响用户体验,于是将异步任务暂时挂起,先运行后面的任务,等异步操作返回了结果,再来执行
所以把任务分为两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous),同步任务是指在主线程上排队的任务,只有前一个任务执行完毕,才会执行后面的任务,异步任务是指不进入主线程,而是进入任务队列(task queue),当任务队列通知主线程某一个异步任务可以执行的时候,才会进入主线程执行。
同步 异步执行机制
2 主线程之外,还有一个任务队列(task queue)
3 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,里面的异步任务就会结束等待状态,进入执行栈,开始执行
4 主线程会不断的重复上面的三部内容
进程
程序运行的实例,同一个程序可以产生多个进程,一个进程可以包含多个线程
线程
操作系统能够进行运算调度的最小单位,一个只能执行一个任务,有自己的调用栈,寄存器环境,同一个进程的线程共享进程资源
浏览器进程:Browser进程,渲染进程(浏览器内核),GPU进程,网络进程,插件进程
渲染进程(浏览器内核)
GUI线程
负责渲染页面,解析html,css,构建DOM树和渲染树,当界面需要重绘(repaint)或者由于某种操作引发回流(reflow)时,该线程就会执行
JS引擎线程
解析JS,和GUI互斥,因为JS也可以操作DOM,如果两个线程同时操作DOM,可能会出现不可预期的结果,所以JS引擎运行期间,GUI处于挂起状态,GUI更新会保存在一个队列中,等JS引擎空闲时,立即执行
定时器线程
定时任务是通过定时器线程运行,他会在定时任务完成之后,通知事件触发线程,往任务队列里添加事件
异步HTTP请求线程
用来处理AJAX请求,当请求完成时,如果有回调函数,就会通知事件触发线程处理
事件触发线程
将满足触发条件的事件,添加到任务队列的末尾
EventLoop
主线程从任务队列中读取事件,这个过程是不断循环的,称为EventLoop(事件循环)
下图可以更好的帮助理解eventLoop,图中的宏任务和微任务下面会说明
微任务 宏任务
微任务(microtasks)
Promise Object.observe(监听对象变化) mutationObserve(一个类,监听DOM结构变化) postMessage(window对象之间用来通信)
宏任务(macrotasks)
script setTimeout setInterval setImmediate I/O UI rendering
异步任务分为宏任务和微任务,首先会执行script宏任务,然后执行完所有微任务之后才会执行宏任务
代码示例
需要注意的是,new Promise()里面的代码是一个参数,是同步执行的,then后面的代码才是异步执行
console.log("start"); setTimeout(() => { console.log("setTimeout1"); }); new Promise((resolve) => { console.log("Promise1"); resolve(); }).then(() => { console.log("then1"); new Promise((resolve) => { resolve(); }).then(() => { console.log("then2"); }); setTimeout(() => { console.log("setTimeout2"); }); }); //结果start Promise1 then1 then2 setTimeout1 setTimeout2
async function async1() { console.log("asyncStart1"); await async2(); console.log("asyncEnd1"); } async function async2() { return Promise.resolve().then((res) => { console.log("asyncPromise2"); }); } console.log("start"); setTimeout(() => { console.log("setTimeout"); }, 0); async1(); new Promise((resolve) => { console.log("promise1"); resolve(); }).then(() => { console.log("promise2"); }); // 结果 start asyncStart1 promise1 asyncPromise2 promise2 asyncEnd1 setTimeout