单线程的Js
首先要明确js单线程运行,所以代码按照出现顺序运行,所以什么是线程?什么是进程?
进程与线程
一个进程包含一个或多个线程
进程
- 进程是资源分配的最小单位,通俗点说,打开一个软件,他就会创建一个或多个进程,打开电脑任务管理器去查看一下进程
线程
- 线程是运行调度的最小单位,通俗点说就是这个软件程序是怎么运行的,是单线程的,按顺从头到尾运行;还是多线程的,两个及以上的线程在同时运行
单线程的原因
如果是多线程的话,那么代表我可以同时执行两个操作,于是在一个线程上添加了一个标签,在另一个线程上又删除了这个标签,这样就产生混乱,所以他必须是单线程执行程序
同步与异步
既然是单线程执行程序,那么看一段代码
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
console.log(3)
// 输出132
如果是按单线程自上而下预定的话,预期是输出123,但是实际输出了132,这种行为又与JS的同步异步操作有关,他与单线程并不冲突
同步
同步代表比虚等待当前操作执行完毕才能执行下一个操作,比如
console.log(1)
console.log(2)
alert('hello')
console.log(3)
//输出123
那么就又存在一个问题:当前操作耗费的时间过长,不能及时运行下面的代码,造成阻塞,比如alert
一个窗口之后,必须点击确定才能运行下面的代码,又或者要读取一个大文件,读取完成之前操作不了任何东西。为了解决这个问题,提出了异步
异步
不会立即执行,而是放入到一个队列中,等待同步操作执行完之后再执行异步。什么操作会是异步操作?常见的有:
- setTimeout,setInterval,setImmediate
- 请求 promise,axios ,ajax
总结
重新看一下这段代码就很容易理解:
console.log(1)
是同步操作输出1- 遇到
setTimeout
是异步操作,放入待执行的队列 console.log(3)
是同步操作输出3- 同步操作都执行完毕了,执行待执行队列中的操作,输出2
上面这个步骤就是一个简单的事件循环机制,继续深入事件循环
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
console.log(3)
事件循环
能触发异步的操作有很多,那么多个存在多个异步操作,这些操作的顺序又是怎样的
console.log(1)
setTimeout(() => console.log(2))
new Promise((resolve) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
}) // 输出1342
如果是异步操作的优先级相同,setTimeout
应该Promise.then
前面执行,但是实际情况不是。由此可见,异步中各种操作的优先级并不是相同的。
Js
对任务又可以分为宏任务和微任务
宏任务(macro-task)
宿主环境提供的方法
- 整体script代码
- setTimeout
- setInterval
- setImmediate
- I/O
- UI render
微任务(micro-task)
js引擎提供的
- process.nextTick
- Promise.then
- Async/Await(实际就是promise)
- MutationObserver(html5新特性)
事件循环过程
- 代码自上而下运行,判断是同步还是异步,同步操作直接运行。异步操作放入任务队列中,在
EventTable
中注册函数,然后推入到EventQueue
中 - 所有的同步操作执行完毕之后,会去任务队列中获取待执行的任务,判断是否存在任务
- 如果队列中存在任务。先执行宏任务,然后执行该宏任务产生的微任务
- 若在执行微任务的过程中,产生了新的微任务,则继续执行微任务,
- 等到该宏任务里面的所有微任务执行完毕之后,会去重新检查任务队列,执行再执行下一个宏任务。
- 所有的宏任务完成之后,则完成了此次的所有任务操作
解析一下这段代码
console.log(1)
setTimeout(function () {
console.log(2)
new Promise(function (resolve) {
console.log(3)
resolve()
}).then(function () {
console.log(4)
})
})
new Promise(function (resolve) {
console.log(5)
resolve()
}).then(function () {
console.log(6)
})
setTimeout(function () {
console.log(7)
new Promise(function (resolve) {
console.log(8)
new Promise(function (resolve) {
console.log(9)
resolve()
}).then(function () {
console.log(10)
})
resolve()
}).then(function () {
new Promise(function (resolve) {
console.log(11)
resolve()
}).then(function () {
console.log(12)
})
console.log(13)
})
})
console.log(14)
// 1 5 14 6 2 3 4 7 8 9 10 11 13 12
- 顺序执行,遇到了三个同步操作 1,5,14,按照出现的顺序输出,两个
setTimeout
创建了两个宏任务,一个new Promise().then
创建了一个微任务 - 整体代码是一个
script
宏任务,然后开始执行这个宏任务中的微任务,输出6 - 当前宏任务里面的微任务也执行完毕,开始执行下一个宏任务,进入到第一个
setTimeout
中,遇到两个同步操作,输出 2,3 - 在这个宏任务里面通过
then
又创建了一个微任务,执行输出4 - 进入到第二个
setTimeout
中执行同步操作,输出7,8,9 - 碰到
then
又创建了一个微任务,输出10, - 上个微任务执行完毕之后进入到外层的
then
里面,这时又创建了一个微任务继续执行输出11,13 - 进入到最后一个
then
里面输出12