目录

  • JavaScript 是一门单线程的语言
  • 一.什么是event Loop的执行机制
    • 练习 异步任务-setTimeout

      • 练习1:
      • 练习2:
      • 练习3:
      • 练习4:
  • 二 事件队列作用
    • 同步任务

      • 例1:
      • 例2:栈溢出(出现死循环):
      • 例3: 同步阻塞
      • 例4: 同步阻塞
    • 应用
      • 1. ajax请求
      • 2. 事件监听器
    • 3. 同步遍历和异步遍历
    • 4. UI渲染
  • 三. Microtasks 和 Macrotasks
    • 属于microtasks的任务有:
    • 属于macrotasks的任务有:
    • 练习
      • 练习1:
      • 练习2
  • 总结

JavaScript 是一门单线程的语言

也就是说,JS一次只能做一件事情。

cpu处理指令速度非常快,远比磁盘I/O和网络I/O速度快,所以一些cpu直接执行的任务就成了优先执行主线任务(即同步任务synchronous),然后需要io返回数据的任务就成了等待被执行的任务(即异步任务asynchronous)

  • 同步任务:在主线程上排队执行的任务,前一个任务执行完毕,才能执行后一个任务;
  • 异步任务:不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

总之:

只要主线程空了,就会去读取”任务队列”,这就是JavaScript的运行机制

一.什么是event Loop的执行机制

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。异步的任务在event table 里面注册函数,当满足触发条件的时候, 进入event queque(就是事件队列)。

(3)一旦"执行栈"中的所有同步任务执行完毕,就去task queue里面查看是否有可执行的异步任务。如果,有就推入到主线程的执行栈(stack)。

(4)主线程不断重复上面的第三步。

参考上图,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

练习 异步任务-setTimeout

练习1:

console.log('script start');
setTimeout(() => {console.log('setTimeout');
}, 1000);
console.log('script end');

输出顺序:

script start
script end
setTimeout

练习2:

console.log('script start');
setTimeout(() => {console.log('setTimeout');
}, 0);
console.log('script end');

输出顺序:

script start
script end
setTimeout

练习3:

console.log('script start');
setTimeout(() => {console.log('setTimeout');
}, 0);// 具体数字不定,这取决于你的硬件配置和浏览器
for (let i = 0; i < 999999999; i++) {// do something
}console.log('script end');

输出顺序:

script start
script end
setTimeout

练习4:

console.log('1');
setTimeout(() => {console.log('2');
}, 0);
setTimeout(() => {console.log('3');
}, 1000);
console.log('4');

输出顺序:

1
4
2
3

二 事件队列作用

由于 JS 是单线程的,同步执行任务会造成浏览器的阻塞,所以我们将 JS 分成一个又一个的任务,通过不停的循环来执行事件队列中的任务。这就使得当我们挂起某一个任务的时候可以去做一些其他的事情,而不需要等待这个任务执行完毕。

同步任务

例1:

window.a = 1;
function foo() {
window.a = window.a + 1;
return foo();
}

foo();

例2:栈溢出(出现死循环):

function foo() {return foo();
}foo();

结果:

例3: 同步阻塞

例4: 同步阻塞

应用

1. ajax请求

通过事件循环机制,我们则不需要等待 ajax 响应之后再进行工作。我们则是设置一个回调函数,将 ajax 请求挂起,然后继续执行后面的代码,至于请求何时响应,对我们的程序不会有影响,甚至它可能永远也不响应,也不会使浏览器阻塞。而当响应成功了以后,浏览器的事件表则会将回调函数添加至事件队列中等待执行。

例:


输出顺序:

Hi
JSConfEU
ajax data

2. 事件监听器

事件监听器的回调函数也是一个任务,当我们注册了一个事件监听器时,浏览器事件表会进行登记,当我们触发事件时,事件表便将回调函数添加至事件队列当中。

例:

3. 同步遍历和异步遍历

// Synchronous 同步遍历
[1, 2, 3, 4].forEach((i) => {console.log(i);
});// Asynchronous 异步遍历
function asyncForEach(array, cb) {array.forEach(() => {setTimeout(cb, 0);});
}asyncForEach([1, 2, 3, 4], (i) => {console.log(i);
});

采用同步遍历的方法,当数组长度上升到3位数的时候,便会出现阻塞,但是异步遍历却不会出现阻塞现象(除非数组长度非常大,那是因为计算机的内存空间不足)。
这是因为同步遍历方法是一个单独的任务,这个任务会将所有的数组元素遍历一遍,然后才会开始下一个任务。而异步遍历的方法将每一次遍历拆分成一个单独的任务,一个任务只遍历一个数组元素,所以在每个任务之间,我们的浏览器可以进行渲染,所以我们不会看见阻塞的情况。

4. UI渲染

DOM 操作会触发浏览器对文档进行渲染,如修改排版规则,修改背景颜色等等,那么这类操作是如何在浏览器当中奏效的?

至此我们已经知道了事件循环是如何执行的,事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,不过在这间,浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能, jQuery 动画在底层均是使用 setTimeout 和 setInterval 来进行实现。

想象一下如果我们同步的执行动画,那么我们不会看见任何渐变的效果,浏览器会在任务执行结束之后渲染窗口。反之我们使用异步的方法,浏览器会在每一个任务执行结束之后渲染窗口,这样我们就能看见动画的渐变效果了。

三. Microtasks 和 Macrotasks

具体到任务队列,又分位 microtasks 和 macrotasks

属于microtasks的任务有:

process.nextTick
promise.then
Object.observe
MutationObserver

属于macrotasks的任务有:

setTimeout
setInterval
setImmediate
I/O
UI渲染

在执行事件时:

  1. 从script(整体代码)开始第一次循环。执行所有主线程的同步函数,遇到异步函数,分别添加到microtasks和macrotasks任务队列
  2. 同步函数执行后,开始执行异步函数中的任务队列,首先执行所有的micro-task所有的micro-task执行完成后,循环执行macro-task任务, 再次执行micro-task,这样一直循环下去.
总之:
整体的js代码在执行完主线程的同步任务,然后有microtask执行microtask,没有microtask执行下一个macrotask,如此往复循环至结束

练习

练习1:

setTimeout(() => {console.log(4);
}, 0);
new Promise(((resolve) => {console.log(1);for (let i = 0; i < 10000; i++) {i == 9999 && resolve();}console.log(2);
})).then(() => {console.log(5);
});
console.log(3);

输出顺序:

1
2
3
5
4

练习2

console.log('start');
const interval = setInterval(() => {console.log('setInterval');
}, 0);
setTimeout(() => {console.log('setTimeout 1');Promise.resolve().then(() => {console.log('promise 3');}).then(() => {console.log('promise 4');}).then(() => {setTimeout(() => {console.log('setTimeout 2');Promise.resolve().then(() => {console.log('promise 5');}).then(() => {console.log('promise 6');}).then(() => {clearInterval(interval);});}, 0);});
}, 0);
Promise.resolve().then(() => {console.log('promise 1');
}).then(() => {console.log('promise 2');
});

输出顺序:

start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6

总结

JavaScript 是一门单线程的语言,但是其事件循环的特性使得我们可以异步的执行程序。这些异步的程序也就是一个又一个独立的任务,这些任务包括了 setTimeout、setInterval、ajax、eventListener 等等。关于事件循环,我们需要记住以下几点:

  1. 事件队列严格按照时间先后顺序将任务压入执行栈执行;
  2. 当执行栈为空时,浏览器会一直不停的检查事件队列,如果不为空,则取出第一个任务;
  3. 在每一个任务结束之后,浏览器会对页面进行渲染;

参考:

  1. 菲利普·罗伯茨:到底什么是Event Loop呢?
  2. whatwg规范
  3. Promise的队列与setTimeout的队列有何关联?
  4. 深入理解 JavaScript 事件循环

转载于:https://www.cnblogs.com/qiqi715/p/10016429.html

Evevt Loop 事件循环相关推荐

  1. 为什么JS是单线程?JS中的Event Loop(事件循环)?JS如何实现异步?setimeout?

    https://segmentfault.com/a/1190000012806637 https://www.jianshu.com/p/93d756db8c81 首先,请牢记2点: (1) JS是 ...

  2. node.js中对Event Loop事件循环的理解

    javascript是单线程的,所以任务的执行都需要排队,任务分为两种,一种是同步任务,一种是异步任务. 同步任务是进入主线程上排队执行的任务,上一个任务执行完了,下一个任务才会执行. 异步任务是不进 ...

  3. Event Loop 事件循环简介

    1.Event Loop? Event Loop 其实也是在面试中经常会出现的一个题,前端程序员回答不上来是正常的,因为 Event Loop 是 C++ 实现的,实现原理和 JavaScript 没 ...

  4. Even Loop(事件循环机制)

    1.什么是事件循环机制 Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制. 2.为什么使用Even Loop 原因:JavaScript 是单线程的.单线程就意味着, ...

  5. Eevnt Loop (事件循环)

    javascript是一门单线程的非阻塞的脚本语言.单线程意味着javascript在执行代码的任何时候,都只有一个主线程来处理所有的任务.非阻塞:是当代码需要进行一项异步任务(无法立刻返回结果,需要 ...

  6. Event Loop事件循环机制

    转载自:阮一峰博客<JavaScript 运行机制详解:再谈Event Loop> 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个 ...

  7. node mysql 事件循环_NodeJs 的 Event loop 事件循环机制详解

    什么是事件轮询 事件循环是 Node.js 处理非阻塞 I/O 操作的机制--尽管 JavaScript 是单线程处理的--当有可能的时候,它们会把操作转移到系统内核中去. 下面的图表显示了事件循环的 ...

  8. JavaScript event loop事件循环 macrotask与microtask

    macrotask  姑且称为宏任务,在很多上下文也被简称为task.例如: setTimeout, setInterval, setImmediate, I/O, UI rendering. mic ...

  9. 【转载】浏览器事件循环机制(event loop)

    首先,本文转自https://juejin.im/post/5afbc62151882542af04112d 当我看完菲利普·罗伯茨的 javascript event loop的演讲的时候,就对于事 ...

  10. 技术干货 | JavaScript 之事件循环(Event Loop)

    导读:学过 JavaScript(下文简称 JS) 的都知道它是一门单线程的.非阻塞的脚本语言.单线程意味着,JS 代码在执行的任何时候,都只有一个主线程来处理所有的任务,这也就意味着 JS 无法进行 ...

最新文章

  1. pandas中DataFrame的ix,loc,iloc索引方式的异同
  2. webpack 编译html模板文件
  3. 《软件工程方法与实践》—— 导读
  4. 数字货币 electron cash钱包 如何长时间存放数字货币
  5. python networkx进行最短路径分析_NetworkX vs Scipy所有最短路径算法
  6. 【Python】字符串和变量拼接的写法
  7. spring学习总结——介绍
  8. 来来来!2021最新Java面经分享
  9. 设计模式系列--Singleton
  10. PMP课程笔记:第9章 项目资源管理
  11. 小熊派鸿蒙开发板,小熊派-鸿蒙#183;季开发板入门(一)
  12. ant design pro中权限组件Authorized的个人学习
  13. uniapp 实现微信聊天效果 阻止input失焦
  14. PostgreSQL11 | pgadmin4基本使用
  15. PPT模板免费下载制作
  16. 百度地图上定位自己所在的位置
  17. 城市餐饮店铺选址分析
  18. 斗鱼直播行业名列前茅 泛娱乐布局成效凸显
  19. 计算机专业郑州粮食批发市场,什么是期货呢????
  20. 鸿蒙系统下矿山! 华为“矿鸿”重磅发布!

热门文章

  1. 易语言单窗口单ip软件源码_易语言助手下载-易语言助手 v3.2.0701 官方版
  2. 编译linux搭建vs2015,使用Vs2015开发linux(centos7)程序
  3. spring-AOP 增强接口Introductions
  4. QFD质量机能展开,了解一下呀!
  5. Bottled Water: 实时集成postgresql与kafka
  6. php内部网,php – WordPress内部网址重写是如何工作的?
  7. 亲爱的我拿什么拯救你的木讷?
  8. 无缝衔接的人会遭报应吗_树洞网-秘密-大树洞 -第6页-在这里,我们为您寄存秘密、心事...
  9. 什么是公司Offer里的RSU?
  10. android 平板 不支持apk,平板电脑上怎么无法打开apk文件