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一变化,界面就立刻重新渲染,效率必然很低,所以浏览器的机制规定界面渲染线程和主线程是互斥的,主线程执行任务时,浏览器渲染线程处于挂起状态。
  • 相关阅读:
    CDQ分治入门
    BSGS算法初探
    简析平衡树(三)——浅谈Splay
    简析平衡树(一)——替罪羊树 Scapegoat Tree
    NOIP2018初赛 解题报告
    【BZOJ1101】[POI2007] Zap(莫比乌斯反演)
    WQS二分学习笔记
    【洛谷2664】树上游戏(点分治)
    同余问题(一)——扩展欧几里得exgcd
    二叉搜索树(BST)学习笔记
  • 原文地址:https://www.cnblogs.com/sunidol/p/11301808.html
Copyright © 2011-2022 走看看