zoukankan      html  css  js  c++  java
  • 理解JS事件循环和异步处理

    JS事件循环

    JS运行环境被称为宿主环境,通常JS会运行在浏览器环境下,配合Web API来操作DOM;有了Node.js后JS可以脱离浏览器而运行了,配合Node API来做一些和操作系统打交道的事情。
    JS是一门单线程语言,它在处理一些比较耗时的任务时采用了异步处理的机制,从而保证程序不会被一个任务给“拖住”。
    为了更好地理解和使用异步处理机制,我们需要深入学习JS的事件循环,其中包括了进程和线程、执行栈、回调函数等知识。

    进程和线程

    当我们打开一个应用程序,我们就说“启动了一个进程”,所以一个进程就代表一个正在运行的应用程序
    在这个应用程序内可能有多个任务要处理,所以这个进程会启动多个线程来分别处理它们,所以一个进程可以包含多个线程
    在浏览器中,可以认为有以下这些线程:

    • JS 引擎:执行执行JS代码
    • GUI:渲染页面
    • 事件监听
    • 计时
    • 网络相关

    执行栈、事件队列、回调函数

    JS引擎通过执行JS代码来使用浏览器的各项功能,如果把浏览器当作一个餐厅,那么JS引擎就是这里唯一的一个服务员。客人点菜后,JS引擎会让厨房做菜,然后它继续接待其他客人,等菜做好时再给客人端上。

    具体来说,内存中有一块叫做执行栈(Call Stack)的区域,JS引擎所执行的代码都是这里的。在调用一个函数前,系统会创建这个函数的执行环境并加入执行栈,执行完毕后就从执行栈移除这个执行环境。我们知道栈的特点是先入后出,也就是说JS引擎总是执行位于最顶上的函数,直到执行栈完全被清空。

    所以一个普通函数的执行过程是简单的入栈、出栈,这叫做同步操作。

    然而有些函数则不一样,它的执行不是由JS引擎这个线程来做的,比如计时函数,它需要我们指定计时多久,以及计时完毕后要执行的函数(回调函数)。当我们调用计时函数时,计时线程就开始工作,无论计时多久(哪怕零秒),计时完成后就会将我们传入的回调函数放入内存中一个叫“事件队列”的区域,等待JS引擎去执行它。

    那么JS引擎什么时候会去执行事件队列中的函数呢?答案是当执行栈完全被清空时。也就是说,当所有同步任务都执行完时,JS引擎才会开始执行事件队列中的函数,这就是以下代码中为什么计时0秒也最后执行的原因:

     console.log(1);
     setTimeout(()=>console.log(2), 0);
     console.log(3);
     console.log(4);
     //1,3,4,2
    

    总之,JS引擎自己能干的事情就会立即干,自己干不了的事情就交给其他线程干,它只在处理完自己的事情后去事件队列看看有没有可以干的事。

    一个任务需要JS引擎之外的线程来处理,那么它的后续任务就需要用一个回调函数来交给JS引擎处理,如果这个后续任务又需要其他线程处理,那么又需要另外的回调函数了。一个异步操作依赖另一个异步操作,而每个异步操作都需要后续处理,这种情况就导致了回调函数的层层嵌套,称为回调地狱

    function TASK() {
      asyncTask1(function () {
          asyncTask2(function () {
              asyncTask3(function () {
                  asyncTask4(function () {
                      //do something with
                  })
              })
          })
      })
    }
    TASK();
    

    ES6的异步处理方式

    ES6将一个可能发生异步操作的事情分为两个阶段,每个阶段都有不同的状态:

    • unsettled:尚未得到结果,状态:pending
    • settled:已经得到结果,无论好坏,状态:
      • resolved:事情以正常的流程走下去并得到了某个结果,后续还可以有进一步处理,表示为thenable
      • rejected:事情在发展过程中出错了,后续只能进行错误处理,表示为catchable

    事情总是从unsettled阶段发展到settled阶段,而且在unsettled阶段可以控制何时通向settled阶段,事情的发展是不可逆转的。在阶段转变的过程中还可以传递一些数据。
    以上概念是学习Promise、Async/Await的关键。

  • 相关阅读:
    poj 1014||hdu 1059 dividing(多重背包 二进制优化)
    Java多线程循环打印ABC的5种实现方法
    java资料搜索网站
    idea 离线安装 lombok插件
    解决TIME_WAIT过多造成的问题
    JAVA线程池详解
    linux vmstat命令
    Mysql慢查询
    sql中强制使用索引
    JAVA 利用 jmc或jvisualvm 监控 本地或者远程JVM
  • 原文地址:https://www.cnblogs.com/MPK-dev/p/12900030.html
Copyright © 2011-2022 走看看