Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

day-17-异步代码执行顺序+ #17

Open
H246802 opened this issue Dec 11, 2018 · 4 comments
Open

day-17-异步代码执行顺序+ #17

H246802 opened this issue Dec 11, 2018 · 4 comments

Comments

@H246802
Copy link
Owner

H246802 commented Dec 11, 2018

以下代码输出的顺序是什么?为什么

setTimeout(() => {
  console.log(4)
  Promise.resolve(8).then(v => {
    console.log(v)
  }).then(() => {
    console.log(9)
  })
}, 0)
setTimeout(() => {
  console.log(10)
  Promise.resolve(11).then(v => {
    console.log(v)
  }).then(() => {
    console.log(12)
  })
}, 0)
new Promise(resolve => {
  console.log(1)
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve()
  }
  console.log(2)
}).then(() => {
  console.log(5)
  Promise.resolve(7)
  .then(v => console.log(v))
}).then(() => {
  console.log(6)
})
console.log(3)
@H246802
Copy link
Owner Author

H246802 commented Dec 11, 2018

@H246802
Copy link
Owner Author

H246802 commented Dec 11, 2018

  • 事件队列(event loop) 先进先出
  • 执行上下文(Execution Context)依次压入执行栈(Execution Context), 栈先入后出
  1. 主线程运行的时候会生成堆(heap)和栈(stack);
  2. js引擎从上到下解析代码,将其中的同步任务按照执行顺序排列到执行栈中;
  3. 当js引擎遇见异步任务时,比如ajax、setTimeout、promise.then 等,会将此类异步任务挂起,并继续执行执行栈中的任务,等异步任务返回结果(例如ajax调用回调函数,setTimeout到时间...)后,再按照执行顺序排列到事件队列中;
  4. 主线程先将执行栈中的同步任务清空,然后检查事件队列中是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务返回结果后排列到事件队列中。
  5. 主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个过程是循环往复的... ...,这个过程被称为“Event Loop 事件循环”。

@H246802
Copy link
Owner Author

H246802 commented Dec 11, 2018

然而事情没那么简单,以上的事件循环过程只是一个宏观的表述,实际上异步任务之间也不相同,执行优先级也有区别。不同的异步任务被分为两类:宏任务(macro task)和微任务(micro task)。我们将经常遇到的异步任务进行分类如下:

宏任务: setTimeout,setInterval,setImmediate,I/O(磁盘读写或网络通信),UI交互事件
微任务: process.nextTick,Promise.then

根据前往我们知道,在时间循环中异步任务会根据返回结果的先后进入event loop。然而根据不同的异步任务分类这也分成了两种类别,对于这些事件分别进入到 event loop 的宏任务和微任务中

当执行栈中的任务清空,主线程会先检查微任务队列中是否有任务,如果有,就将微任务队列中的任务依次执行,直到微任务队列为空,之后再检查宏任务队列中是否有任务,如果有,则每次取出第一个宏任务加入到执行栈中,之后再清空执行栈,检查微任务,以此循环... ...

同一次事件循环中,微任务永远在宏任务之前执行。

@H246802
Copy link
Owner Author

H246802 commented Dec 11, 2018

根据前文资料,我们可以看到这段代码中有这各种宏任务和微任务

// a
setTimeout(() => {
  // e
  console.log(4)
  // f
  Promise.resolve(8).then(v => {
    console.log(v)
  }).then(() => {
    // g
    console.log(9)
  })
}, 0)
// b
setTimeout(() => {
  // h
  console.log(10)
  Promise.resolve(11).then(v => {
    // i
    console.log(v)
  }).then(() => {
    // j
    console.log(12)
  })
}, 0)
// c
new Promise(resolve => {
  // k
  console.log(1)
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve()
  }
  // l
  console.log(2)
}).then(() => {
  // m
  console.log(5)
  // n
  Promise.resolve(7)
  .then(v => console.log(v))
}).then(() => {
  // o
  console.log(6)
})
// d
console.log(3)

// 
/*
a b 两部分代码都会被添加到 event loop 中,等到主线程任务完成后再执行,可以先看后面的部分

从 c 开始
c -> k
js 解释器开始执行 k ,打印 1
for 循环结束后,promise 状态变更为 fullfilled
执行 l,打印 2
外部的同步代码开始执行 d,打印 3
then 方法 resolved 的 异步回调函数开始执行 m,打印 5,
n 部分的 promise 对象状态立刻变更为 fullfilled,相应的回调函数开始打印 7
外部 c 的第二个 then 方法开始执行,异步回调函数执行 o,打印 6

此时主线程的任务执行完毕,开始执行 event loop 中 a、b
a 中代码顺序执行,依次打印 4 8 9
b 中代码顺序执行,依次打印 10 11 12

经测试 控制台中打印顺序为 1 2 3 5 7 6 4 8 9 10 11 12
node 环境中为 1 2 3 5 7 6 4 10 8 11 9 12
a、b 部分执行顺序不同应该与不同的 v8 引擎对 task queue microtask 的理解不同
*/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant