zoukankan      html  css  js  c++  java
  • JS异步执行机制

    前言

    • JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面。

    • 异步加载也叫非阻塞模式加载

    • 同步或非同步,表明着是否需要将整个流程按顺序地完成

    • 阻塞或非阻塞,意味着你调用的函数会不会立刻告诉你结果

    javascript的单线程和异步

    • js是单线程语言(能提高效率。作为浏览器脚本语言,js的主要用途是与用户互动,操作DOM。而这也就决定它只能为单线程,否则会带来很复杂的同步问题),浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的

    • js是单线程语言,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。
      详情见:https://www.cnblogs.com/woodyblog/p/6061671.html

    • 虽然js是单线程,但是我们可以将任务分成两类

    1.同步任务:需要执行的任务在主线程上排队,依次执行
    2.异步任务:没有立马执行但是需要被执行的任务,放在任务队列(task queue,一个事件的队列或者消息的队列)里面。

    Event Loop事件循环

    打开网站的时候,网页的渲染其实是一堆同步任务,比如页面骨架和页面元素的渲染,但是像图片音乐等占用资源大耗时久的任务就是异步任务。
    主线程:

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

    js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

    • 4.主线程从"任务队列"中读取事件,这个过程是循环不断的,形成event loop(事件循环)
      举例:
      任务队列里放的是ajax这类的任务,是交给浏览器发起HTTP请求去执行的,当有了返回结果就会在任务队列中增加一个事件,表示该ajax请求已经返回了结果,任务队列里的任务和js主线程是同时执行的。不影响js是单线程的这个结论,只能说浏览器还会提供接口来供js。
      图片说明
      图例:同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。 当指定的事情完成时,Event Table会将这个函数移入Event Queue。 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

    setTimeout

    在使用setTimeout的时候,经常会发现设定的时间与自己设定的时间有差异,贴段代码看一下

    setTimeout(() => {
        task();
    },3000)
    console.log('执行console');
    // 执行console 
    // task()
    

    setTimeout是一个异步的所以会先执行console这个同步任务。但是,如果改成下面这段会发现执行时间远远超过预定的时间。

    setTimeout(() => {
        task()
    },3000)
    sleep(10000000)
    

    我们来看一下是怎么执行的:

    • task()进入到event table里面注册计时
    • 然后主线程执行sleep函数,但是非常慢。计时任然在继续
    • 3秒到了。task()进入event queue 但是主线程依旧没有走完
    • 终于过了10000000ms之后主线程走完了,task()进入到主线程
      所以可以看出其真实的时间是远远大于3秒的

    解释几个容易困惑的问题

    • 1. setTimeout(f1,0) ,f1是不是立刻执行?
      答案是不一定,要看主线程内的命令是否已经执行完了。这个任务会在主线程最早可得的空闲时间执行,就是主线程的任务执行结束之后立马执行
    console.log('先执行这里');
    setTimeout(() => {
        console.log('执行啦')
    },0);
    // 先执行这里
    // 执行啦
    

    HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。

    • 2. Ajax请求是否异步??
      ajax请求内容的时候是异步的,当请求完成后,会触发请求完成的事件,然后把回调函数放入callback queue,等到主线程执行该回调函数时还是单线程的。
    • 3. 界面渲染线程是单独开辟的线程,是不是DOM一变化,界面就立刻重新渲染?
      如果DOM一变化,界面就立刻重新渲染,效率必然很低,所以浏览器的机制规定界面渲染线程和主线程是互斥的,主线程执行任务时,浏览器渲染线程处于挂起状态。
  • 相关阅读:
    HAproxy 1.5 dev14 发布
    IBM/DW 使用 Java 测试网络连通性的几种方法
    Skype 4.1 Linux 发布,支持微软帐号登录
    Dorado 7.1.20 发布,Ajax的Web开发平台
    Aspose.Slides for Java 3.0 发布
    开发版本 Wine 1.5.18 发布
    BitNami Rubystack 开始支持 Ruby 2.0
    XWiki 4.3 正式版发布
    Silverlight实例教程 Out of Browser的Debug和Notifications窗口
    Silverlight实例教程 Out of Browser与Office的互操作
  • 原文地址:https://www.cnblogs.com/sunidol/p/11301808.html
Copyright © 2011-2022 走看看