一:同步和异步
同步:按顺序执行
异步:先执行一部分,等待结果再执行后续代码
- 1、计时器
- 2、ajax
- 3、读取文件
同步程序执行完成后,执行异步程序
二:单线程
js是单线程的,一个任务执行之后才能执行另一个任务
举例:为什么计时器是不准的,for循环2000次之后才会执行定时器,因为是单线程的
for(let i=0;i<2000;i++){consol.log(1)}
settimeout(()=>{console.log(2)},0)
三:Nodejs中process.nextTick和setImmediate
- 1.同步
- 2.nextTick
- 3.异步
- 4.setImmediate(当前事件循序结束执行,就是任务队里里面空的时候会执行)
//同步执行之后,异步执行之前执行
process.nextTick(()=>{
console.log(6)
})
// 异步代码执行之后
setImmediate(()=>{
console.log(1)
})
四:什么是事件循环
计时器不准:是到点了之后才把后面的代码插入到任务队列里面。而且是等待队里里面之前的运行完才开始运行这个,
setImmediate(()=>{console.log(1)})
//当前事件循环结束后,没有执行5是因为,这个时候任务队列里面5还没被放进来
process.nextTick(()=>{console.log(2)})
console.log(3)
setTimeout(()=>{console.log(4)},0)
setTimeout(()=>{console.log(5)},1000)
setTimeout(()=>{console.log(6)},0)
console.log(7)
// 372461 5
五:宏任务和微任务
宏任务
- 计时器
- ajax
- 读取文件
- script标签
- 鼠标事件,键盘事件
微任务
- promise.then
- process.nextTick
执行顺序
- 1.同步程序
- 2.Process.nextTick
- 3.微任务
- 4.宏任务
- 5.setImmediate
setImmediate(()=>{console.log(1)})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>console.log(4),100)
console.log(5)
new Promise((resolve)=>{
console.log(6) //promise里面的是同步的
resolve()
}).then(()=>{
console.log(7) //then里面的是异步的
})
process.nextTick(()=>console.log(8))
// 25687314
// 解释一下:
// 首先执行同步代码 2 5 6
// 然后执行 nextTick 8
// 然后执行异步代码,
// 异步里面先执行微任务, 7
// 然后再执行宏任务 3
// 最后执行setImmedaiat 1
// 最后执行 4 因为100毫秒才进入执行队里里面,进来的慢,所以最后执行
setImmediate(()=>{console.log(1)})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>console.log(4),100)
new Promise((resolve)=>{
console.log(5) //promise里面的是同步的
resolve()
}).then(()=>{
console.log(6) //then里面的是异步的
}).then(()=>{
console.log(7)
})
console.log(8)
process.nextTick(()=>console.log(9))
//258 9 67 314
setImmediate(()=>{console.log(1)})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>console.log(4),100)
new Promise((resolve)=>{
console.log(5) //promise里面的是同步的
//resolve() //只有调用resolve的时候才会执行then,没有resolve,then不会执行
}).then(()=>{
console.log(6) //then里面的是异步的
}).then(()=>{
console.log(7)
})
console.log(8)
process.nextTick(()=>console.log(9))
//258 9 314
六:promise与async函数
promise
只有调用resolve的时候才会执行then,没有resolve,then不会执行
let p = new Promise((resolve)=>{
resolve('hi')
})
p.then((res)=>{
console.log(1)
console.log(res)
})
// 1 hi
let p = new Promise((resolve)=>{
// resolve('hi')
})
p.then((res)=>{
console.log(1)
console.log(res)
})
// 什么也不打印
async返回值是promise对象,then传的参数,就是return的值,async函数就是promise对象的简写
function fun1(){
return new Promsie((resolve)=>{
resolve(1)
})
}
// async函数就是promise对象的简写
async function fun(){
return 1
}
let a = fun()
console.log(a)// Promise { 1 }
fun().then((data)=>{console.log(data)}) // 1
async函数里面加await await函数后面加promise对象
let p1 = new Promise((resolve)=>{
resolve(1)
})
let p2 = new Promise((resolve)=>{
resolve(2)
})
async function fun(){
// a = await 后面加promise对象 await+promise对象就可以拿到resolve的值
let a = await p1
let b = await p2
console.log(a)
console.log(b)
}
fun()
// 1
// 2
await下面的代码看做是then中的代码,那就是异步中的微任务
async function fun1(){
let data = await fun2()
console.log(data)
//await下面的代码看做是then中的代码,那就是异步中的微任务
}
async function fun2(){
console.log(200)// 同步代码
return 100
}
fun1()
// 200
// 100
console.log(1)
async function async1(){
await async2()
console.log(2)
}
async function async2(){
console.log(3)
}
async1()
setTimeout(function(){
console.log(4)
},0)
new Promise(resolve=>{
console.log(5)
resolve()
}).then(function(){
console.log(6)
}).then(function(){
console.log(7)
})
console.log(8)
// 1358 267 4
// 1358同步 267 微任务 4宏任务
★ 执行栈
执行代码。根据先进后出的原则,后执行的函数会先弹出栈;因为最后执行的函数,执行完,就弹出了,然后下面的也就是之前的函数了,一个个按顺序再弹出被
★ 浏览器中的 Event Loop
涉及面试题:异步代码执行顺序?解释一下什么是 Event Loop ?
执行 JS 代码的时候其实就是往执行栈中放入函数,当遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。
事件循环
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。下面来看以下代码的执行顺序:
console.log('script start') //1
async function async1() {
await async2()
console.log('async1 end') //7
}
async function async2() {
console.log('async2 end') //2
}
async1()
setTimeout(function() {
console.log('setTimeout') //8
}, 0)
new Promise(resolve => {
console.log('Promise') //3
resolve()
})
.then(function() {
console.log('promise1') //5
})
.then(function() {
console.log('promise2') //6
})
console.log('script end') //4
// 1.script start =>2. async2 end => 3.Promise => 4.script end
【1234都是直接就执行的了】
=> 5.promise1 => 6.promise2
【56都是promise回调函数的了】
=>7.async1 end => 8.setTimeout
【7 await 8 settimeout】
await 看成是让出线程的标志。
然后当同步代码全部执行完毕以后,就会去执行所有的异步代码,那么又会回到 await 的位置执行返回的 Promise 的 resolve 函数,这又会把 resolve 丢到微任务队列中,接下来去执行 then 中的回调,当两个 then 中的回调全部执行完毕以后,又会回到 await 的位置处理返回值,这时候你可以看成是 Promise.resolve(返回值).then(),然后 await 后的代码全部被包裹进了 then 的回调中,所以 console.log('async1 end') 会优先执行于 setTimeout。
所以 Event Loop 执行顺序如下所示:
- 首先执行同步代码,这属于宏任务,【script宏任务中的同步代码】
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是
setTimeout中的回调函数
所以以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。
微任务包括 process.nextTick ,promise ,MutationObserver。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
这里很多人会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。
main thread 主线程和 call-stack 调用栈
Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
-
JS 调用栈
JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。
-
同步任务、异步任务
JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈【后进先出】中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)【跟调用栈不一样的另外一个东西,先进先出】中等待主线程空闲的时候,也就是栈【后进先出的同步任务】内被清空的时候,被读取到栈【调用栈】中等待主线程执行。任务队列是先进先出的数据结构。
【解释一下:就是先执行同步任务,同步任务在调用栈里面,是后进先出,同时异步任务有了结果之后是放在任务队列也叫消息队列里面的,是先进先出,当栈里面空了,队列里面的东西再进去,队列先进先出先入栈后出栈】
【再解释一下:调用栈里面执行同步任务,等全部执行完了,栈空了,再把消息队列里面的回调结果放进栈里】
-
Event Loop
调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。
事件循环机制,为什么要分微任务和宏任务,微任务为什么比宏任务先执行?
【因为微任务再微任务队列里面,宏任务再宏任务队列里面,先执行微任务队列里面的数据,微任务空了之后再执行宏任务的数据,但是如果不如说异步的返回比较慢的话,微任务队列里面有没有数据了,那可能就先执行宏任务了】 promise ajax这种的返回结果放到微任务队列里面,执行栈,没了之后去微任务队列里面找,没了再去宏任务队列里面找,
宏任务:计时器、ajax、dom操作、读取文件、 微任务:promise的then