• JS高级 之 事件循环


    目录

    一、进程和线程

    1. 概念

    2. 操作系统的工作方式

    单核CPU和多核CPU

    以单核CPU为例

    3. 浏览器中的JavaScript线程

    二、浏览器的事件循环

    1. 定时器栗子 🌰

    01 - 代码

    02 - 解析

    2. 监听点击栗子 🌰 

    01 - 代码

    02 - 解析

    三、宏任务和微任务 

    面试题一

    代码

    解析

            流程一 : 执行全局script代码

            流程二 : 清空微任务队列

            流程三 : 执行第一个宏任务后清空队列

            流程四 : 执行第二个宏任务

    面试题二

    代码

    解析

    面试题三

    代码

    解析

    面试题四

    代码

    解析

     


    一、进程和线程

    1. 概念

    线程和进程是操作系统中的两个概念:

    进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式

    线程(thread):操作系统能够运行运算调度的最小单位,通常情况下它被包含在进程

    通俗的说就是:

    进程:可以认为,启动一个应用程序,就会默认启动一个进程(也可能是多个进程)

    线程:一个进程中,都会启动至少一个线程用来执行程序中的代码,这个线程被称之为主线程,所以也可以说进程是线程的容器

    再用一个形象的例子解释:

    • 操作系统类似于一个大工厂
    • 工厂中里有很多车间,这个车间就是进程
    • 每个车间可能有一个以上的工人在工厂,这个工人就是线程

    2. 操作系统的工作方式

    单核CPU和多核CPU

    操作系统是如何做到同时让多个进程(边听歌、边写代码、边查阅资料)同时工作呢?

    以单核CPU为例

    • 这是因为CPU的运算速度非常快,它可以快速的在多个进程之间迅速的切换
    • 当我们进程中的线程获取到时间片时,就可以快速执行我们编写的代码
    • 对于用户来说是感受不到这种快速的切换

    3. 浏览器中的JavaScript线程

    JavaScript是单线程(可以开启workers)

    但是JavaScript的线程应该有自己的容器进程:浏览器或者Node 

     

    注 : workers 可以让js开启多线程

    浏览器是多进程的 : 

    • 目前多数的浏览器其实都是多进程
    • 当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出
    • 每个进程中又有很多的线程,其中包括执行JavaScript代码的线程

    JavaScript的代码执行是在一个单独的线程中执行的 : 

    • 这就意味着JavaScript的代码,在同一个时刻只能做一件事
    • 如果这件事是非常耗时的,就意味着当前的线程就会被阻塞

    真正耗时的操作,实际上并不是由JavaScript线程在执行的 : 

    • 浏览器的每个进程是多线程的,那么其他线程可以来完成这个耗时的操作
    • 比如网络请求、定时器、页面点击事件,只需要在特性的时候执行应该有的回调即可

    二、浏览器的事件循环

    1. 定时器栗子 🌰

    如果在执行JavaScript代码的过程中,有异步操作

    比如中间插入了一个setTimeout的函数调用

    这个函数被放到入调用栈中,执行会立即结束,并不会阻塞后续代码的执行

    01 - 代码

    1. let name = 'coder';
    2. function bar() {
    3. console.log('bar function');
    4. }
    5. // 交给浏览器的其他线程执行
    6. setTimeout(() => {
    7. console.log('setTimeout');
    8. }, 10000);
    9. function foo() {
    10. bar();
    11. }
    12. foo();

    02 - 解析

    2. 监听点击栗子 🌰 

    01 - 代码

    1. <script>
    2. const btn = document.querySelector('button');
    3. // 1. 绑定点击事件
    4. btn.onclick = function () {
    5. console.log('btn click event');
    6. };
    7. console.log('Hello World');
    8. // 2. 定时器1
    9. setTimeout(() => {
    10. console.log('0s后的setTimeout');
    11. }, 0);
    12. console.log('-------------');1
    13. // 3. 定时器2
    14. setTimeout(() => {
    15. console.log('10s后的setTimeout');
    16. }, 10000);
    17. console.log('-------------');
    18. script>

    02 - 解析

    三、宏任务和微任务 

    事件循环中并非只维护着一个队列,事实上是有两个队列 :

    宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等

    微任务队列(microtask queue):Promise的then回调、 Mutation Observer API ( 监听dom变化的 )、queueMicrotask ( 直接把回调函数加入到微任务队列中 ( ) => { } )等

    两个队列的优先级 : 

    1. main script中的代码优先执行(编写的顶层script代码)
    2. 执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
    3. 也就是宏任务执行之前,必须保证微任务队列是空的,如果不为空,那么就优先执行微任务队列中的任务(回调)

    面试题一

    代码

    1. console.log('script start'); // 1
    2. setTimeout(function () {
    3. console.log('setTimeout1'); // 8
    4. new Promise(function (resolve) {
    5. resolve();
    6. }).then(function () {
    7. new Promise(function (resolve) {
    8. resolve();
    9. }).then(function () {
    10. console.log('then4'); // 10
    11. });
    12. console.log('then2'); // 9
    13. });
    14. });
    15. new Promise(function (resolve) {
    16. console.log('promise1'); // 2
    17. resolve();
    18. }).then(function () {
    19. console.log('then1'); // 5
    20. });
    21. setTimeout(function () {
    22. console.log('setTimeout2'); // 11
    23. });
    24. console.log(2); // 3
    25. queueMicrotask(() => {
    26. console.log('queueMicrotask1'); // 6
    27. });
    28. new Promise(function (resolve) {
    29. resolve();
    30. }).then(function () {
    31. console.log('then3'); // 7
    32. });
    33. console.log('script end'); // 4

    解析

            流程一 : 执行全局script代码

            流程二 : 清空微任务队列

            流程三 : 执行第一个宏任务后清空队列

            流程四 : 执行第二个宏任务

    面试题二

    代码

    1. console.log('script start');
    2. function requestData(url) {
    3. console.log('requestData');
    4. return new Promise((resolve) => {
    5. setTimeout(() => {
    6. console.log('setTimeout');
    7. resolve(url);
    8. }, 2000);
    9. });
    10. }
    11. function getData() {
    12. console.log('getData start');
    13. requestData('why').then((res) => {
    14. console.log('then1-res:', res);
    15. });
    16. console.log('getData end');
    17. }
    18. getData();
    19. console.log('script end');

    解析

    面试题三

    代码

    1. console.log('script start');
    2. function requestData(url) {
    3. console.log('requestData');
    4. return new Promise((resolve) => {
    5. setTimeout(() => {
    6. console.log('setTimeout');
    7. resolve(url);
    8. }, 2000);
    9. });
    10. }
    11. // 2.await/async
    12. async function getData() {
    13. console.log('getData start');
    14. // await下面的代码,相当于一同在then方法中,是一起的
    15. const res = await requestData('why');
    16. console.log('then1-res:', res);
    17. console.log('getData end');
    18. }
    19. getData();
    20. console.log('script end');

    解析

    面试题四

    代码

    1. async function async1() {
    2. console.log('async1 start');
    3. await async2();
    4. console.log('async1 end');
    5. }
    6. async function async2() {
    7. console.log('async2');
    8. }
    9. console.log('script start');
    10. setTimeout(function () {
    11. console.log('setTimeout');
    12. }, 0);
    13. async1();
    14. new Promise(function (resolve) {
    15. console.log('promise1');
    16. resolve();
    17. }).then(function () {
    18. console.log('promise2');
    19. });
    20. console.log('script end');

    解析

     

     

     

     

  • 相关阅读:
    Java 线程池异步任务
    ZKP3.2 Programming ZKPs (Arkworks & Zokrates)
    nRF5340(入门篇)之1.1 nrf5340芯片简介
    cs与msf联动
    数字图像处理(2)像素邻域、领接、通路与距离
    第一章 信息化和信息系统
    《Data Cleansing for Models Trained with SGD》笔记
    MyBatis 篇
    MySQL的组成与三种log
    线性表的查找
  • 原文地址:https://blog.csdn.net/a15297701931/article/details/126378333