javascript中的异步、微任务、宏任务、Eventloop事件循环机制详解

js执行机制

js是单线程的,也就代表js只能一件事情一件事情执行,那如果一件事情执行时间太久,后面要执行的就需要等待,需要等前面的事情执行完成,后面的才会执行。

所以为了解决这个问题,js委托宿主环境(浏览器)帮忙执行耗时的任务,执行完成后,在通知js去执行回调函数,而宿主环境帮我们执行的这些耗时任务也就是异步任务

js本身是无法发起异步的,但是es5之后提出了Promise可以进行异步操作

执行流程

  1. - 主线程先判断任务类型 
    1. 如果是同步任务,主线程自己执行
    2. 如果是异步任务,交给宿主环境(浏览器)执行
  2. - 浏览器进行异步任务的执行,每个异步执行完后,会将回调放进任务队列,先执行完成的先放进任务队列,依次放入
  3. - 等主线程任务全部执行完后,发现主线程没有任务可执行了,会取任务队列中的任务,由于任务队列里是依次放入进来的,所以取得时候也会先取先进来的,也就是先进先出原则
  4. - 在任务队列中取出来的任务执行完后,在取下一个,依次重复,这个过程也称为eventLoop 事件

javascript中的异步、微任务、宏任务、Eventloop事件循环机制详解_第1张图片

微任务与宏任务

概念:

宿主环境提供的叫宏任务,由语言标准提供的叫微任务

  • 宿主环境: 简单来说就是能使javascript完美运行的环境,只要能完美运行javascript的载体就是javascript的宿主环境。目前我们常见的两种宿主环境有浏览器和node。 我们都知道window是我们一直使用的全局对象,但其实global 是 javascript 运行时所在宿主环境提供的全局对象,在node出生前这个对象一直都存在于概念里。直到node的出现才使我们真正看到了global。 global 是 javascript 运行时所在宿主环境提供的全局对象,在浏览器中,没有实现global对象,而是通过window对象来指向global对象,代替global成为全局对象。因为浏览器暴露了一系列操作 DOM, Location, History 等 Api 供 javascript 调用,而这些操作对象在global中是不存在的。对于node来说,它不需要DOM这些操作,用到的只是javascript的原声功能。 宿主环境内所有的内建或自定义的变量/函数都是 global/window 这个全局对象的属性/方法,而由宿主环境提供的也叫宏任务。
  • 语言标准: 我们都知道JavaScript是一种编程语言,但其实JavaScript由ECMA制定标准,称之为ECMAScript,所以由语言标准提供的就是微任务,比如ES6提供的promise。 ,promise是ES6语言标准提供的,定时器是宿主环境提供的,所以promise会比定时器更早执行。

执行顺序

  1. 先执行宏任务
  2. 宏任务执行完后看微任务队列是否有微任务
  3. 没有微任务执行下一个宏任务
  4. 有微任务将所有微任务执行
  5. 执行完微任务,执行下一个宏任务

javascript中的异步、微任务、宏任务、Eventloop事件循环机制详解_第2张图片

相信接下来的案例一定对你有帮助
案例1:

console.log(1)
setTimeout(function(){
  console.log(2)
}, 0)
new Promise(function(resolve){
  console.log(3)
  resolve()
}).then(function(){
  console.log(4)
})
console.log(5)



答案:1、3、5、4、2

解析:

主线程判断是同步代码还是异步代码

console.log(1)  // 同步任务
setTimeout(function(){
  console.log(2) // 异步任务:宏任务
}, 0)
new Promise(function(resolve){
  console.log(3) // 同步任务
  resolve()
}).then(function(){
  console.log(4) // 异步任务:微任务
})
console.log(5)  // 同步任务



执行同步任务

console.log(1)  // 同步任务
console.log(3) // 同步任务
console.log(5)  // 同步任务



执行异步任务:微任务

console.log(4) // 异步任务:微任务


执行异步任务:宏任务

console.log(2) // 异步任务:宏任务


案例2:

注意点:await的执行顺序为从右到左,会阻塞后面的代码执行,但并不是直接阻塞await的表达式

await下面(下面不是右面)的代码可以理解为promise.then(function(){ 回调执行的 })

async function async1() {
  console.log('async1 start')
  await async2()
  // await后面的代码可以理解为promise.then(function(){ 回调执行的 })
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')
setTimeout(function() {
  console.log('setTimeout')
}, 0)

async1()

console.log('script end')



答案:script start、async1 start、async2、script end、async1 end、setTimeout

解析:

主线程判断同步异步

async function async1() {
  console.log('async1 start')
  await async2() // 同步任务
  // await后面的代码可以理解为promise.then(function(){ 回调执行的 })
  console.log('async1 end') // 异步任务:微任务
}

async function async2() {
  console.log('async2')
}
console.log('script start') // 同步任务
setTimeout(function() {
  console.log('setTimeout') // 异步任务:宏任务
}, 0)

async1() // 同步任务

console.log('script end') // 同步任务


执行同步任务

console.log('script start')
console.log('async1 start')
console.log('async2')
console.log('script end')


执行异步任务:微任务

console.log('async1 end')
console.log('setTimeout')


案例3:

 

console.log(1)
setTimeout(function(){
  console.log(2)
}, 2000)
new Promise(function(resolve){
  console.log(3)
  resolve()
}).then(function(){
  console.log(4)
})

setTimeout(function(){
  console.log(5)
  new Promise(function(resolve){
    console.log(6)
    resolve()
  }).then(function(){
    console.log(7)
  })
}, 3000)

setTimeout(function(){
  console.log(8)
  new Promise(function(resolve){
    console.log(9)
    resolve()
  }).then(function(){
    console.log(10)
  })
}, 1000)

相信看到这里你一定懂了 不需要解析了吧

答案:1、3、4、8、9、10、2、5、6、7

你可能感兴趣的:(微任务与宏任务,javascript,前端)