线程
javascript
是一门单线程、异步、非阻塞、弱类型脚本语言。
JS的单线程是指一个浏览器进程中只有一个JS的执行线程,同一时刻内只会有一段代码在执行,但是浏览器的渲染进程是多线程的,而异步机制是浏览器的两个或以上常驻线程共同完成的。
同步异步 阻塞非阻塞
老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
——来源网络,作者不明。
事件循环
首先简单了解JS执行顺序
step 1. 读入第一个代码块。
step 2. 语法分析,有错则报语法错误,并跳转到step5。
step 3. 对var变量和function定义做“预编译处理”(预解析)。
step 4. 执行代码段,有错则报错(比如变量未定义)。
step 5. 如果还有下一个代码段,则读入下一个代码段,重复step2。
step 6. 结束。
事件循环
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一但"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
5.只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复,这种机制就被称为事件循环(event loop)机制
宏任务 微任务
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
宏任务 macro-task(Task)
一个event loop有一个或者多个task队列。task任务源非常宽泛,比如ajax的onload,click事件,基本上我们经常绑定的各种事件都是task任务源,还有数据库操作(IndexedDB ),需要注意的是setTimeout、setInterval、setImmediate也是task任务源。总结来说task任务源:
- script
- setTimeout
- setInterval
- setImmediate
- I/O
- requestAnimationFrame
- UI rendering
微任务 micro-task(Job)
microtask 队列和task 队列有些相似,都是先进先出的队列,由指定的任务源去提供任务,不同的是一个 event loop里只有一个microtask 队列。另外microtask执行时机和Macrotasks也有所差异
- process.nextTick
- promise
- Object.observe
- MutationObserver
宏任务和微任务的区别
- 宏队列可以有多个,微任务队列只有一个,所以每创建一个新的settimeout都是一个新的宏任务队列,执行完一个宏任务队列后,都会去checkpoint 微任务。
- 一个事件循环后,微任务队列执行完了,再执行宏任务队列
- 一个事件循环中,在执行完一个宏队列之后,就会去check 微任务队列
自我提示
console.log("start")
setTimeout(()=>{
console.log(5)
},0)
Promise.resolve().then(()=>{
setTimeout(()=>{
console.log(1)
},0)
console.log(2)
}).then(()=>{
console.log(3)
})
console.log(4)
// process.nextTick(()=>{
// console.log(6)
// })
console.log("end")
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。