宏任务微任务

本文介绍了JavaScript中的事件循环机制,包括宏任务(如setTimeout、setInterval)和微任务(如Promise、MutationObserver)的执行顺序。在每轮事件循环中,主线程先执行同步任务,然后执行宏任务,宏任务内部又会触发微任务,所有微任务都会在当前宏任务执行完毕后立即执行。此外,文章还提到了未处理的Promiserejection的处理方式。
摘要由CSDN通过智能技术生成

宏任务(Macrotask)

  • 浏览器中:I/O、setTimeout、setInterval、requestAnimationFrame

  • Node中:I/O、setTimeout、setInterval、setImmediate

微任务(Microtask)

  • 浏览器中:MutationObserver、Promise.then/.catch/.finally

  • Node中:process.nextTick 、Promise.then/.catch/.finally

微任务与宏任务执行顺序

 JavaScript是单线程的,常用的任务分为同步任务和异步任务。在每轮事件循环中,主线程会先执行完同步任务,再执行异步任务。

  整体JavaScript代码将作为一个宏任务执行,先将同步任务进入主线程执行,异步任务进入事件(Event Table)并注册回调函数(如:success、then、catch等)。当异步事件完成,回调函数进入事件队列等待被调用。而来自不同任务源的任务会进入不同的任务队列。其中setTimeout与setInterval是同源的。

  js引擎Monitoring Process进程会不断的检查主线程执行栈是否为空,一旦为空,就会检查事件队列中是否有等待被调用的函数,如果有,主线程将依次读取回调函数并调用。否则执行下一轮事件循环。

  在每轮事件循环中微任务队列的优先级高于宏任务队列。微任务队列中排队的所有微任务都在同一周期内处理,而这些微任务本身也可以将其他微任务添加到微任务队列中中执行,只有这些微任务全部执行完成时,才会执行下一个宏任务。

  new Promise()在实例化的过程中所执行的代码都是同步执行的,而.then、.catch 和 .finally都是异步执行的。

console.log('script start');  // 同步setTimeout(function () {
  console.log('setTimeout1');  // 异步
}, 300);

setTimeout(function () {
  console.log('setTimeout2');  // 异步
}, 0);

newPromise(resolve => {
  resolve()
  console.log('promise1');  // 同步
}).then(() => {
  console.log('promise2');  // 异步
})

console.log('script end');  // 同步// script start// promise1// script end// promise2// setTimeout2// setTimeout1

  在微任务中注册新的微任务,其执行依然会在宏任务之前执行。

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

newPromise(resolve => {
  resolve()
  console.log('promise1');
}).then(() => {
  console.log('promise2');
  newPromise(resolve1 => {
    resolve1()
    console.log('promise3');
  }).then(() => {
    console.log('promise4');
  })
})
// promise1// promise2// promise3// promise4// setTimeout复制代码

  process.nextTick()先于Promise.then()执行, setTimeout()与setImmediate()执行顺序取决于setTimeout的执行周期与设备性能。

console.log('golbol');

setTimeout(function () {
  console.log('timeout1');
  process.nextTick(function () {
    console.log('timeout1_process');
  })

  newPromise(function (resolve) {
    console.log('timeout1_promise');
    resolve();
  }).then(function () {
    console.log('timeout1_then')
  })
})

setImmediate(function () {
  console.log('immediate1');
  process.nextTick(function () {
    console.log('immediate1_process');
  })

  newPromise(function (resolve) {
    console.log('immediate1_promise');
    resolve();
  }).then(function () {
    console.log('immediate1_then')
  })
})

process.nextTick(function () {
  console.log('process1');
})

newPromise(function (resolve) {
  console.log('promise1');
  resolve();
}).then(function () {
  console.log('promise1_then')
})

setTimeout(function () {
  console.log('timeout2');
  process.nextTick(function () {
    console.log('timeout2_process');
  })

  newPromise(function (resolve) {
    console.log('timeout2_promise');
    resolve();
  }).then(function () {
    console.log('timeout2_then')
  })
})

process.nextTick(function () {
  console.log('process2');
})

newPromise(function (resolve) {
  console.log('promise2');
  resolve();
}).then(function () {
  console.log('promise2_then')
})


setImmediate(function () {
  console.log('immediate2');
  process.nextTick(function () {
    console.log('immediate2_process');
  })

  newPromise(function (resolve) {
    console.log('immediate2_promise');
    resolve();
  }).then(function () {
    console.log('immediate2_then')
  })
})
  • 整体代码作为宏任务执行:

宏任务代码

微任务队列

整体代码

- 输出:global

- 将setTimeout标记为timeout1添加到宏任务队列中

- 将setImmediate标记为immediate1添加到宏任务队列中

- 将process标记为process1添加到微任务队列中

- 输出:promise1。将promise标记为promise1添加到微任务队里

- 将setTimeout标记为timeout2添加到宏任务队列中

- 将process标记为process2添加到微任务队列中

- 输出:promise2。将promise标记为promise2添加到微任务队里

- 将setImmediate2标记为immediate2添加到宏任务队列中

```

global、promise1、promise2

```

宏任务队列

微任务队列

timeout1、timeout2

process1、process2

immediate1、immediate2

promise1、promise2

  主进程事件执行完,执行微任务队列,process.nextTick比promise.then先执行

process1、process2、promise1_then、promise2_then
  • 执行setTimeout宏任务队列

宏任务队列

微任务队列

timeout1、timeout2

immediate1、immediate2

  分别执行宏任务队列setTimeout任务源

  1. 输出:timeout1

  1. 将process标记为timeout1_process添加到微任务队列中

  1. 输出:timeout1promise。将promise标记为timeout1then添加到微任务队里

  1. 输出:timeout2

  1. 将process标记为timeout2_process添加到微任务队列中

  1. 输出:timeout2promise。将promise标记为timeout2then添加到微任务队里

timeout1、timeout1_promise、timeout2、timeout2_promise

  主进程事件执行完,执行微任务队列

宏任务队列

微任务队列

timeout1process、timeout2process

immediate1、immediate2

timeout1then、timeout2then

  执行微任务队列:

timeout1_process、timeout2_process、timeout1_then、timeout2_then
  • 执行setImmediate宏任务队列

宏任务队列

微任务队列

immediate1、immediate2

  分别执行宏任务队列setImmediate任务源

  1. 输出:immediate1

  1. 将process标记为immediate1_process添加到微任务队列中

  1. 输出:immediate1promise。将immediate标记为immediate1then添加到微任务队里

  1. 输出:immediate2

  1. 将process标记为immediate2_process添加到微任务队列中

  1. 输出:immediate2promise。将promise标记为immediate2then添加到微任务队里

immediate1、immediate1_promise、immediate2、immediate2_promise

  主进程事件执行完,执行微任务队列

宏任务队列

微任务队列

immediate1process、immediate2process

immediate1then、 immediate2then

  执行微任务队列:

immediate1_process、immediate2_process、immediate1_then、immediate2_then

整体输出

global、promise1、promise2、

process1、process2、promise1_then、promise2_then、

timeout1、timeout1_promise、timeout2、timeout2_promise、

timeout1_process、timeout2_process、timeout1_then、timeout2_then、

immediate1、immediate1_promise、immediate2、immediate2_promise、

immediate1_process、immediate2_process、immediate1_then、immediate2_then

模拟调用堆栈

let macrotask = []
let microtask = []
let call_stack = []

function setMicroTask(cb) {
  microtask.push(cb)
}

function setMacroTask(cb) {
  macrotask.push(cb)
}

global.setTimeout = function setTimeout(cb, time) {
  macrotask.push(cb)
}

function runHandler() {
  for (var i = 0; i < call_stack.length; i++) {
    eval(call_stack[i])
  }
}

function run(cb) {
  macrotask.push(cb)
  for (let i = 0; i < macrotask.length; i++) {
    eval(macrotask[i])()
    if (microtask.length != 0) {
      for (let j = 0; j < microtask.length; j++) {
        eval(microtask[j])()
      }
      microtask = []
    }
  }
}

call_stack.push(`console.log('script start');`)
call_stack.push(`setTimeout(function() {
  console.log('setTimeout');
}, 0);`)

call_stack.push(`setMicroTask(()=> {
  console.log('micro1')
  setMicroTask(()=> {
    console.log('micro2')
  })
})`)

call_stack.push(`console.log('script end');`)

run(runHandler)
call_stack.push(`console.log('script start');`)
call_stack.push(`setTimeout(function() {
  console.log('setTimeout');
}, 0);`)

call_stack.push(`setMicroTask(()=> {
  console.log('micro1')
  setMicroTask(()=> {
    console.log('micro2')
  })
})`)

call_stack.push(`console.log('script end');`)

run(runHandler)
script start
script end
micro1
micro2
setTimeout

未处理的rejection

  "未处理的rejection"是指在微任务队列结束时未处理的promise错误。

let promise = Promise.reject(newError('Promise Failed!'))

window.addEventListener('unhandledrejection', event => {
  alert(event.reason)
})

// Uncaught (in promise) Error: Promise Failed!

  创建了一个rejected的promise并没有处理错误,所以会打印一个“未处理的 rejection”。添加.catch()就会解决。

let promise = Promise.reject(newError('Promise Failed!'))
promise.catch(err =>alert('caught'))


window.addEventListener('unhandledrejection', event => {
  alert(event.reason)
})

// caught

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值