单线程的原因
1 .js主要的用途是与用户互动,以及操作dom,这决定了只能是单线程,否则会带来非常复杂的同步问题,比如一个线程提添加dom,一个减少dom。应该以哪个为准
2 .web worker:允许js脚本创建多个子线程,但是子线程完全受主线程控制,且不得操作dom
任务队列
1 .单线程就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务,但是前一个任务耗时很长,后一个任务就不得不等待
2 .同步任务:在主线程上的排队执行的代码,只有前一个任务执行完毕,才能执行后一个任务
3 .异步任务:不进入主线程,进入任务队列的任务,只有异步队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 onload,onclik,onerror
js中的异步事件
1 .io设备的事件,比如ajax。io设备完成一项任务,就在任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取“任务队列”。就是读取里面有哪些事件
2 .用户产生的事件,鼠标点击,页面滚动等dom操作
3 .只要制定过回调函数,这些事件就会进入“任务队列”,等待主线程读取
4 .所谓的回调函数,就会被主线程挂起来的代码。异步任务必须制定回掉函数,当主线程开始执行异步任务,就是执行对应的回调函数
5 .任务队列是一个先进先出的数据结构。排在前面的事件,有限被主线程读取,主线程的读取过程上基本是自动的,只要执行栈一空,任务队列上的第一位事件就会自动进入主线程。
6 .只有一种情况是那样:比如定时器功能,主线程首先要检查一下执行时间,某些时间只有到了规定的时间,才能返回主线程
异步任务的运行机制
1 .所有同步任务都在主线程执行,形成一个执行栈。还会产生堆,但是不知道堆里面是啥任务
2 .主线程之外,存在一个任务队列,只要异步任务有了运行结果。就在“任务队列”之间放置一个事件,通知主线程进行执行这个事件。这些都是栈中的代码操作的。调用各种外部api,再任务队列中加入各种事件
3 .一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。对应的异步事件,如果可以执行,结束等待状态,进入执行栈,开始执行
4 .主线程是不断重复上面的第三步的。只要主线程空了,就回去读取“任务队列”,这就是js的运行机制。
5 .同步任务的代码中是在异步任务(任务队列)之前执行。所以有时候顺序其实并不是很重要
6 .
定时器
1 .除了防止异步任务,“任务队列”还可以放置定时事件,即某些代码再多少时间之后执行
2 .他是在任务队列末尾插入一个事件,要等同步任务和“任务队列”现有的事件都处理完之后,才会得到执行
setTimeout(function(){console.log(1);}, 0);
console.log(2)
//2,1一定是这样的输出,就是只有再执行完第二行之后,系统才会去执行“任务队列”中的回调函数
// 0的含义是里面的任务可以再主线程最早的空闲的时间执行,尽可能早的执行
3 .h5规定了setTimeout的第二个参数的最小,不得低于4mm,如果低于这个值会自动添加
4 .setTimeout只是将事件插入了“任务队列”,必须等到当前代码《执行栈执行完毕,主线程才会取执行他制定的回掉函数。要是当前diamagnetic耗时很长,可能需要等好久,所以是没有方法保证,回调函数一定会在setTimeout制定的时间执行,一般是比这个时间长
nodeJs的Event Loop
1 .运行机制
1 .v8解析js脚本
2 .解析后的代码,调用 Node API
3 .libuv 库负责件该任务分配给不同的线程,形成一个Event Loop,以异步的方式将任务的执行结果返回v8引擎
4 .v8引擎将结果返回用户
2 .process.nextTick 方法可以再当前“执行栈”的尾部,下一次Event Loop(主线程)之前触发,也就是说。他是会一定再异步任务发生之前触发的,不论有几个这样的函数,也不论里面有多少嵌套
3 .setImmediate:多个process.nextTick语句总是在当前“执行栈”一次执行完,多个setImmediate可能需要多次loop才能执行完。防止递归process.nextTick函数无限循环下去,主线程根本无法读取事件队列
4 .process.nextTick制定的回调函数总是再本次事件循环触发,而setTmmediate制定的是在下次“事件循环”触发,所以很显然,前者总是比后者发生的早,而且执行效率更高
第二波
1 .同步,异步关注的是消息通知机制
1 .同步再发出调用之后,没有结果前是不会返回的,一旦调用返回,就得到返回值,调用者主动等待这个调用结果
2 .异步是发出调用后,调用者不会立刻得到结果,而是被调用者通过状态或者回调函数来处理这个调用
2 .同步任务:主线程上执行的任务,只有前一个任务执行完毕,下一个任务才能执行
3 .异步任务:是指不进入主线程,而是进入任务队列的任务,只有主线程任务执行完毕,任务队列的任务才会进入主线程执行。
4 .在js的运行过程中,真正负责执行js代码的始终只有一个线程,通常被称为是主线程,各种任务都是会用排队的方法同步执行。这种方式最常见的一个问题就是当你尝试执行一段非常耗时的同步代码,浏览器就没办法同时渲染GUI,导致界面失去响应,也就是被阻塞
5 .事件循环可以让js做到既是单线程,又不会阻塞的核心机制,也是js并发模型的基础,用来协调各种事件
6 .事件循环其实是一种实现异步的机制
7 .事件循环并不是js标准定义的,而是html标准定义的
8 .事件循环是宿主环境提供的,比如浏览器
微任务和宏任务
1 .都属于任务队列里面的分类
2 .微任务
1 .process.nextTick
2 .原生promise
3 .mutationObserver
4 .当前栈执行结束之后立即需要执行的任务。例如需要对一系列的任务做出回应,或者是需要异步执行的任务
5 .当前执行栈中的代码执行完毕之后,会在执行宏任务队列之前先看看微任务队列中有没有任务,如果有会先将微任务队列中的任务清空才会执行宏任务队列
3 .宏任务
1 .setTimeout
2 .setInterval
3 .等待执行栈和微任务队列都执行完毕才会执行,并且在执行完每一个宏任务之后,都会去看看微任务队列有没有新增加的任务。如果有,先将微任务队列中的任务清空,才会继续执行下一个宏任务
4 .
执行引擎
1 .Engine
2 .编译饼执行js代码,完成内存分配,垃圾回收等任务
3 .chrome和node 都是用了V8 引擎,v8实现并提供了ECMAScript标准中所有数据类型,操作符,对象和方法
4 .
执行环境
1 .Runtime
2 .为js提供一些对象或者机制,是他能够和外界交互
3 .比如dom机制
4 .chrome提供了window,dom
任务队列
1 .一个Event Loop会有一个或者多个task 队列。都是一些队列的数据结构,存放着不同任务源的任务
1 .dom操作
2 .用户交互 user intreaction
3 .网络请求
4 .history api 操作
5 .task source的定义非常宽泛,常见的鼠标,键盘事件,ajax,数据库操作等,以及定时期相关的setTimeout,setInterval等等都属于task source,所有来自task soucrce 的任务都会被放进对应的task队列中等待处理
6 .来自相同任务源的任务,必定官方在同一个任务队列中
7 .同一个任务源内的任务是按照顺序执行的
8 .对于不同的任务队列,浏览器会进行调度,允许优先执行来自特定任务源的任务,来优化任务。比如同时遇到鼠标,键盘和网络请求都有各自的任务队列。当两者同时存在的时候,浏览器可以优先从用户交互相关的任务队列中挑选任务并执行,从而保证流畅的用户体验
9 .
微任务队列
1 .在html标准中,并没有明确规定微任务,但是通常有以下几种
2 .promise 实现机制
3 .mutationObserver
4 .微任务执行的时机
1 .某个任务执行完毕时
2 .进入脚本执行的清理阶段
3 .创建和插入节点时
4 .解析XML文档时
5 .用处
1 .提供异步队列机制:把数据的变化部分缓冲在一个event loop中发生所有的数据变更,从而避免有机会在正真更新UI前,去除重复触发的数据变更,避免dom的不必要更新
2 .借助promise.then,MutationObserver等实现Vue.nextTick()方法使得Vue有能力让ui尽早得到更新,在一轮event loop轮次而非下一轮中就更新完毕
3 .
js运行机制
1 .主线程不断循环
2 .同步任务,创建执行上下文,按顺序进入执行栈
3 .异步任务
1 .同步执行这段代码
2 .将相应的task 添加到event loop的任务队列
3 .其他线程来执行具体的异步操作。尽管js是单线程的,但是浏览器内核是多线程的,他会将GUI渲染,定时器触发,HTTP请求等工作交给专门的线程来处理
4 .当主线程执行完当前执行栈中的所有任务,就回去读取event loop的任务队列,取出执行任务
event loop处理模型
1 .执行任务:从任务队列中取出一个最老的任务并执行,如果没有就跳过
2 .执行微任务
3 .进入更新渲染阶段
1 .设置 Performance api中的now()的返回值。
2 .遍历本次event loop相关的documents,执行更新渲染,在执行迭代的过程中,浏览器会根据各种因素来判断是否要跳过本次更新
3 .当浏览器确认继续本次更新后,处理更新渲染相关工作
4 .触发各种事件 resize,scroll,media quaries ,css animations,fullscreen api-执行animation frame callbacks,window.requestAnimationFrame就在这里--个更新intersection observations,用于图片懒加载,更新渲染和UI,最终将结果提交到界面上