Open Hibop opened 6 years ago
先从一道简单的面试题说起. 下面代码的输出结果是?
setTimeout(function(){console.log("three");}, 0); Promise.resolve().then(function(){ console.log( "two" ); }); console.log("one");
答案是: one two three! 看似一个简单到不能再简单的面试题, 涉及的知识点很多. 作为面试题短小精悍, 用来摸底候选人, 真的不错.
下面来解析一下涉及的知识点
每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的 执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。
函数调用栈(Call Stack): 在 JavaScript 程序中,必定会产生多个执行上下文,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。栈的数据结构是 先进后出, 最后进入调用栈中的函数会最先执行.
JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。队列的数据结构式 先进先出, 最后进入队列的函数最后执行. 任务队列有两种:
macro-task(宏任务 Task)
script(整体代码) setTimeout/setinterval setlmmediate I/O 操作 UI rendering
micro-task (微任务 Job)
process.nextTick Promise.then MutationObserve async/await
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
Event Loop
Javscript 的执行机制是: 首先事件循环从宏任务队列开始, 这个时候宏任务队列中, 只有一个script(整体代码)任务. 每一个任务的执行顺序, 都依靠函数调用栈来搞定, 而当遇到任务源时, 则会先分发任务到对应的队列中去, 先执行调用栈中的函数, 当调用栈中的执行上下文全部被弹出, 只剩下全局执行上下文的时候, 就开始执行 Job 执行队列, Job 执行队列执行完成后就开始执行 Task 执行队列, 先进入的先执行, 后进入的后执行, 无论是 Task 还是 Job 都是通过函数调用栈来执行. Task 执行完一个, JavaScript 引擎会继续检查是否有 Job 需要执行. 就形成了 Task--Job--Task--Job 的循环, 这就行形成了事件循环 ( Event Loop).
好了, 回到一开始的面试题上来. 我们来大致走一下它的执行流程:
当前 Task 执行 (整体代码), 首先 setTimeout 的 callback 被添加到 macro-task 队列中 Promise.then 的 callback 被添加到 micro-task 队列中 代码 console.log("one") 进入调用栈, 输出 one 当前全局上下文全部被弹出, 开始执行 Job 队列, 输出 Job 队列的 two Job 队列执行完成, 开始执行 Task 队列, 输出 setTimeout 回调中的 three
http://www.codeceo.com/javascript-threaded.html
https://github.com/JChehe/blog/blob/master/posts/%E5%85%B3%E4%BA%8EJavaScript%E5%8D%95%E7%BA%BF%E7%A8%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BA%8B.md
先从一道简单的面试题说起. 下面代码的输出结果是?
答案是: one two three! 看似一个简单到不能再简单的面试题, 涉及的知识点很多. 作为面试题短小精悍, 用来摸底候选人, 真的不错.
下面来解析一下涉及的知识点
函数调用栈(Call Stack)
每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的 执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。
函数调用栈(Call Stack): 在 JavaScript 程序中,必定会产生多个执行上下文,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。栈的数据结构是 先进后出, 最后进入调用栈中的函数会最先执行.
队列数据结构(Queue)的事件
JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。队列的数据结构式 先进先出, 最后进入队列的函数最后执行. 任务队列有两种:
macro-task(宏任务 Task)
micro-task (微任务 Job)
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
Event Loop
Javscript 的执行机制是: 首先事件循环从宏任务队列开始, 这个时候宏任务队列中, 只有一个script(整体代码)任务. 每一个任务的执行顺序, 都依靠函数调用栈来搞定, 而当遇到任务源时, 则会先分发任务到对应的队列中去, 先执行调用栈中的函数, 当调用栈中的执行上下文全部被弹出, 只剩下全局执行上下文的时候, 就开始执行 Job 执行队列, Job 执行队列执行完成后就开始执行 Task 执行队列, 先进入的先执行, 后进入的后执行, 无论是 Task 还是 Job 都是通过函数调用栈来执行. Task 执行完一个, JavaScript 引擎会继续检查是否有 Job 需要执行. 就形成了 Task--Job--Task--Job 的循环, 这就行形成了事件循环 ( Event Loop).
好了, 回到一开始的面试题上来. 我们来大致走一下它的执行流程:
当前 Task 执行 (整体代码), 首先 setTimeout 的 callback 被添加到 macro-task 队列中 Promise.then 的 callback 被添加到 micro-task 队列中 代码 console.log("one") 进入调用栈, 输出 one 当前全局上下文全部被弹出, 开始执行 Job 队列, 输出 Job 队列的 two Job 队列执行完成, 开始执行 Task 队列, 输出 setTimeout 回调中的 three