• event loop的理解


    最开始见到event loop这个字眼是在一次笔试上,请简要描述你对event loop的理解。作为前端最底层的小白,当时看到event loop都不知道是什么意思。回来之后赶紧科普一下,event loop翻译成中文是事件循环。题目大概考察的是对js中事件机制的理解。翻阅众多博客,有长话短说的也有详细讲解的。以下是自己看完博客后的理解。

    一、js是单线程的

    js语言的一大特点就是单线程,也就是说js在同一时间只能做一件事,那么有人会问,为什么不是多线程。这样js代码执行起来岂不是更快。

    首先,js的这个特性与它的用途有关。js作为浏览器脚本语言,js的主要功能是与用户进行交互,以及操作dom。如果js是多线程,比如有两个线程,一个线程是在dom上添加了一段文字,另一个线程在dom上减少了一段文字,两个线程同时执行,那么浏览器不知道以哪个线程为准,执行就会出现问题。为了避免这些问题的发生,js从诞生起就一直是单线程,这已经成了js的一大特点,将来也不会改变。

    为了利用多核cpu的计算能力,H5新增了Web Worker标准,允许js创建多个线程,但是子线程完全受主线程的控制,且不可以操作dom,所以在本质上并没有改变js单线程的特性。

    二、任务队列(Task Queue)

    单线程意味着所有的任务都需要排队依次执行,前一个任务结束后才开始进行下一个任务。如果前一个任务执行的时间非常长,那么后一个任务也得必须等待。

    如果是因为运算量过大导致cpu忙不过来倒是可以理解,但是更多时候cpu处于闲置状态,因为IO设备(输入输出设备)很慢(比如ajax从服务器获取数据),必须等待结果出来以后才往下继续进行。

    js的设计者意识到这样的问题,主线程可以完全不管IO设备,挂起处于这样状态的任务,先执行排在后边的任务。等到IO设备返回了结果,再回过头去执行挂起的任务。

    于是,js中任务分为两种,同步任务(synchronous)和异步任务(asychronous)。同步任务指的是在主线程上排队执行的任务,只要前一个执行完了,后一个才去执行。异步任务指的是不进入主线程、而进入“任务队列(task queue)”的任务,只有“任务队列”通知主线程,某个任务队列可以执行了,该任务才会进入主线程去执行。

    具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步的任务异步执行)

    1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
    2. 除主线程之外,还存在一个“任务队列(task queue)”,只有异步任务有了运行结果,就会在“任务队列”中放置一个事件。
    3. 一旦执行栈中所有同步任务执行完毕,系统就会读取“任务队列”,看看里边有哪些事件。那些对应的异步任务,才会结束等待状态,进入执行栈,开始执行。
    4. 主线程不断重复执行第三步

    只要主线程空了,就回去读取“任务队列”,这就是js的运行机制。这个过程会不断重复。

    三、事件和回调函数

    “任务队列”是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就会在“任务队列”中添加一个事件,表示相关的异步任务可以进入“执行栈”了。主线程读取“任务队列”,就是在读取里边有哪些事件。

    “任务队列”中的事件,除了IO设备的事件以外,还包括用户产生的事件(鼠标点击,页面滚动等)。只要指定过回调函数,这些事件发生时就会进入“任务队列”,等待主线程的读取。

    四、event loop

    主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为event loop(事件循环)

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

    执行栈中的代码(同步任务),总是在读取“任务队列”(异步任务)之前执行。例如

    var req = new XMLHttpRequest();
    req.open("get",url);
    req.onload = function(){};
    req.onerror = function(){};
    req.send();

    上述代码中,req.send()是ajax向服务器发送数据,它是一个异步任务,意味着只有当前脚本全部执行完毕后,系统才回去读取“任务队列”。所以下边是等价写法

    var req = new XMLHttpRequest();
    req.open("get",url);
    req.send();
    req.onload = function(){};
    req.onerror = function(){};

    上边的意思就是说,指定函数回调的部分(onload和onerror),在send方法的前边或后边无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们才去执行“任务队列”。

    五、定时器

    除了放置异步任务的事件,“任务队列”还可以放置定时事件,即指定某些代码在多少时间后执行。这叫做定时器功能,也就是定时执行的代码。定时器功能主要由setTimeOut和SetInterval这两个函数来完成。它们的内部运行机制完全一样,它们两个区别在于前者执行一次,后者可以反复执行。

    console.log(1);
    setTimeout(function(){console.log(2)},1);
    console.log(3);
    //打印结果
    //1,3,2

    因为console.log(2)是延迟1毫秒执行的。但是,如果把1写成0,

    console.log(1);
    setTimeout(function(){console.log(2)},0);
    console.log(3);

    执行结果依然是1,3,2

    第二个参数设置为0,表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。

     说明:setTimeout(fn,0)含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说尽可能早的执行。它在“任务队列”的尾部添加一个事件,因此要等到同步任务和“任务队列”现有的事件都处理完,才会得到执行。

    H5标准说明了,setTimeout第二个参数的最小值(最短间隔)不得低于4毫秒。如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。

    setTimeout只是将事件插入到“任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它的指定的回调函数。要是当前代码执行时间太长,有可能要等很久,所以没办法保证,一定会在setTimeout指定时间执行。

    六、node.js中的event loop

    暂无

  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/zmyxixihaha/p/10878316.html
Copyright © 2020-2023  润新知