加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Event Loop

发布时间:2020-12-15 07:56:49 所属栏目:Java 来源:网络整理
导读:Event Loop 为什么JavaScript是单线程 其最初的用途来决定的:与浏览器交互。 试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这

Event Loop

为什么JavaScript是单线程

其最初的用途来决定的:与浏览器交互。
试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,javascript选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。
一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!

宏任务(macro-task)和微任务(micro-task)

JavaScript的事件分两种,宏任务(macro-task)和微任务(micro-task)

  • 宏任务:包括整体代码script,setTimeout,setInterval, setImmediate(node独有), I/O, UI rendering
  • 微任务:Promise.then(非new Promise),process.nextTick(node独有),Object.observe(废弃),MutationObserver

  • async函数使用==await以后得所有语句会被放入一个回调函数中,放入微任务中==
  • 2个微任务的优先级,==promise高于async==

浏览器js引擎事件循环机制

事件的执行顺序的基础是 ==先执行宏任务,后执行微任务==
任务可以有同步任务和异步任务,同步和异步任务分别进入不同的执行"场所"

  1. 同步的进入主线程;
  2. 异步的进入Event Table并注册函数; 异步事件完成后,会将回调函数放入Event Queue中(宏任务和微任务是不同的Event Queue);
  3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数。
  4. 回调函数中可能还会包含不同的任务,因此会循环执行上述操作。
  • 宏任务同步代码 => 微任务(队列) => 宏任务(队列)
    => 微任务(队列) ...
setTimeout(function() {
    console.log('setTimeout');
},1000)

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

console.log('console');
  • 首先setTimeout,放入Event Table中,1秒后将回调函数放入宏任务的Event Queue
  • new Promise 同步代码,立即执行console.log(‘promise‘),然后看到微任务then,因此将其放入微任务的Event Queue
  • 接下来执行同步代码console.log(‘console‘)
  • 主线程的宏任务执行完毕,接下来要执行微任务,因此会执行Promise.then,到此,第一轮事件循环执行完毕
  • 第二轮事件循环开始,先执行宏任务(的事件队列),即setTimeout的回调函数,然后查找是否有微任务,没有,事件循环结束

事件循环,先执行宏任务,其中同步任务立即执行,异步任务,加载到对应的的Event Queue,所有同步宏任务执行完毕后,如果发现微任务的Event Queue中有未执行的任务,会先执行其中的任务,这样算是完成了一次事件循环。接下来查看宏任务的Event Queue中是否有未执行的任务,有的话,就开始第二轮事件循环,依此类推。

来至于谷友的一到面试题

<script>
async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
},0);
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
  }).then(function() {
    console.log('promise2');
  });
console.log('script end');
</script>

// script start => async1 start => async2 => promise1 => script end => promise2 => async1 end => setTimeout
  1. 整个代码块作为一个宏任务,进入主线程
  2. 看到函数申明但没有执行,遇到函数console.log执行,输出script start
  3. 遇到setTimeout() ,把它的回调函数放入宏任务(setTimeout1)。

    宏任务 微任务
    setTimeout1
  4. 遇到执行async1(),进入async的执行上下文之后,遇到console.log输出async1 start
  5. 然后遇到await async2(),由于()的优先级高,所有先执行async2(),进入async2()的执行上下文。
  6. 看到console.log输出async2,之后没有返回值,结束函数,返回undefined,返回async1的执行上下文的await undefined,由于async函数使用await后得语句会被放入一个回调函数中,所以把下面的放入微任务中。

    宏任务 微任务
    setTimeout1 async1=> awati 后面的语句
  7. 结束async1,返回全局上下文,遇到Promise构造函数,里面的函数立马执行, 输出promise1,之后的回调函数进入微任务

    宏任务 微任务
    setTimeout1 async1=> awati 后面的语句
    ` ` new Promise() => 后的then
  8. 执行完Promise(),遇到console.log,输出script end,这里一个宏任务代码块执行完毕。
  9. 在主线程执行的过程中,事件触发线程会一直监听异步事件,当异步事件处理完成后,把它的回调函数放入事件队列,等待执行。
  10. 主线程现在空闲下来后,执行事件队列中的微任务,然后继续向下执行,遇到new Promise()后面的回调函数,执行代码,输出promise2(这里2个微任务的优先级,promise高于async)。
  11. 看到async1中await后面的回调函数,执行代码,输出async1 end

    宏任务 微任务
    setTimeout1
  12. 此时微任务中的队列为空,开始执行队列中的宏任务,进入一个新的代码块。遇到console.log,输出setTimeout


原文链接:https://www.jianshu.com/p/de7aba994523

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读