“JavaScript 是单线程、异步、非阻塞、解释型脚本语言。”
进程:浏览器一个页面就是新的一个进程,进程是CPU资源分配的最小单位(系统会给它分配内存);javascript
线程:线程包含在每一个进程内,线程是CPU调度的最小单位(线程是创建在进程的基础上的一次程序运行单位,一个进程中能够有多个线程);java
永远只有JS引擎(JS内核)线程在执行JS脚本程序, 负责解析执行Javascript脚本程序的主线程(例如V8引擎)
因为JavaScript是可操纵DOM的,若是在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程先后得到的元素数据就可能不一致了。
所以为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时当即被执行。node
单线程语言:JavaScript 的设计就是为了处理浏览器网页的交互(DOM操做的处理、UI动画等),决定了它是一门单线程语言。【处理任务是一件接着一件处理,从上往下顺序执行】web
若是有多个线程,它们同时在操做 DOM,那网页将会一团糟。
当遇到计时器、DOM事件监听或者是网络请求的任务时,JS引擎会将它们直接交给** webapi,也就是浏览器提供的相应线程(如定时器线程为setTimeout计时、异步http请求线程处理网络请求)去处理,而JS引擎线程继续后面的其余任务,这样便实现了异步非阻塞**。后端
JavaScript 中有同步代码与异步代码。api
因为Es6 和node出现产生的微任务promise
console.log('script start') setTimeout(function() { console.log('timer over') }, 0) Promise.resolve().then(function() { console.log('promise1') }).then(function() { console.log('promise2') }) console.log('script end') // script start // script end // promise1 // promise2 // timer over
JS引擎线程首先执行主代码块。浏览器
每次执行栈执行的代码就是一个宏任务,包括任务队列(宏任务队列)中的,由于执行栈中的宏任务执行完会去取任务队列(宏任务队列)中的任务加入执行栈中,即一样是事件循环的机制。网络
在执行宏任务时遇到Promise等,会建立微任务(.then()里面的回调),并加入到微任务队列队尾。多线程
microtask必然是在某个宏任务执行的时候建立的,而在下一个宏任务开始以前,浏览器会对页面从新渲染(task >> 渲染 >> 下一个task(从任务队列中取一个))。同时,在上一个宏任务执行完成后,渲染页面以前,会执行当前微任务队列中的全部微任务。
也就是说,在某一个macrotask执行完后,在从新渲染与开始下一个宏任务以前,就会将在它执行期间产生的全部microtask都执行完毕(在渲染前)。
这样就能够解释 "promise 1" "promise 2" 在 "timer over" 以前打印了。"promise 1" "promise 2" 作为微任务加入到微任务队列中,而 "timer over" 作为宏任务加入到宏任务队列中,它们同时在等待被执行,可是微任务队列中的全部微任务都会在开始下一个宏任务以前都被执行完。
在node环境下,process.nextTick的优先级高于Promise,也就是说:在宏任务结束后会先执行微任务队列中的nextTickQueue,而后才会执行微任务中的Promise。
吃透这些例子 包你掌握js执行顺序及promise知识:
https://www.jianshu.com/p/e585e737fb6f