• js 异步实现与编程


    同步

    同步

    同步是代码从上到下依次执行,上一个任务结束后,才能执行下一个任务。

    如下图所示,任务1执行完后,再执行任务2,任务2执行完后再执行任务3,依次类推...

    同步优势

    同步是任务有序进行,不会造成资源上处理上的混乱。

    1.任务有序进行较好的处理了任务之间的依赖性,如后一个任务需要前一个任务的结果。

    2.如果多个任务处理同一个资源,不会造成资源处理的混乱。

    var a = 1;

    function task1(){

      console.log(a);

      for(var i = 0; i <10000;i++){

        a++;

         }

      console.log(a);

    }

    function task2(){

      console.log(a);

      for(var i = 0; i <10000;i++){

        a--;

         }

      console.log(a);

    }

    task1();

    task2();

    task1、task2都操作变量a。先执行task1, 执行完 task1后得到一个a的结果值。然后task2处理task1处理的结果值。

    如果task1与task2不是同步的,task1没有执行完,去执行task2,task2执行一会,再去执行task1,... ,可能a的值都不是task1、 task2想要的结果。

    同步弊端

    同步上从上到下依次执行的,必须等到上个任务完成,才能处理下一个任务。如果上一任务占用的时间比较长,会让下一个任务长时间等待。

    异步

    异步

    1.任务不是按照顺序依次执行的。

    2.不等到上个任务执行完,执行下个任务。

    如果前一个任务没有执行完,下一个任务可能已经开始,下一个任务执行一段时间,又去执行上一个任务...,看起来像多个任务同时执行。

    如下图所示,任务1执行一段时间后,去执行任务2,然后再执行任务1,再执行任务2,执行任务3。任务1与任务2不是按照顺序执行的,各自占用一段时间执行,好像任务1与任务2,同时执行的。

    异步优势

    异步不用等待上一个任务执行完,就可以执行下一个任务,不用较长时间等待上一任务完成。

    异步弊端

    如果使用异步 处理同一个资源,可能造成资源的混乱。如上面的task1、 task2共同处理a值。

    js引擎使同步与异步协作

    js引擎在解析javascript时,同时使用了同步与异步的思想。

    同步

    线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段),进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。浏览器中有多个线程协作共同完成前端界面的展示。

    js引擎是单线程的,从上到下依次执行代码,依次处理任务。这样不会导致资源的混乱。浏览器是根据DOM树来显示页面元素的,如果一个任务处理DOM,另外一个任务也在处理DOM,那么就会造成处理DOM混乱。

    如下图所示,任务A处理DOM一段时间,然后任务A停止处理DOM,任务B开始处理这个DOM,任务B处理DOM停止,任务A继续处理DOM。那么任务A接收的不是自己处理DOM的预期结果,而是任务B处理后的结果。

    如下代码规规矩矩从上到依次执行,先执行A任务,A任务执行完了再执行B任务。保证资源的处理不会混乱。

    var num = 1;

    function A(){

      num = 2;

    }

    function B(){

      num = 3;

    }

    A();

    B();

    如果上一个任务执行时间比较长,导致下一个任务不能执行,导致浏览器卡顿怎么办?

    如网络请求数据,网络请求数据的过程用的时间比较长,就需要一直等待网络资源的请求,直到请求完成。这样会让后面的执行等待很长时间。为此,浏览器使用了基于异步的事件驱动的处理。

    异步

    javascript引擎线程从上到下依次执行javascript代码(回调不执行)。回调被其他线程(事件触发线程、计数器触发线程)触发后,放入一个异步队列中。

    即浏览器是把上一个任务未完成的部分(作为子任务)存起来,继续执行下面的任务当浏览器空闲时,再去处理未完成的部分。这样既能保证任务能够顺序执行,又不会因为上一个任务时间过长,导致下个任务需要长时间等待。

    线程间执行是异步的,回调被触发时,可能javascript引擎线程正在从上到下执行代码,也可能已经执行完毕。

    javascript引擎线程从上到下执行完代码后,开始查找异步队列中是否有任务,如有任务则按照队列的顺序依次执行任务。

    $(document).ready(function(){
       $('div').click(function(){
          console.log('click');
       })
     })
    setTimeout(function(){
       console.log('timer');
    },100);
    var count = 0;
    setInterval(function(){
       count++;
       console.log('inter'+count);
    },1000);

    打印结果:

    图1 异步队列

    100ms后,计时器触发线程触发回调,把回调放入队列中;每个1000ms周期,计时器触发线程触发回调,Iterval的回调放入队列中;点击div时,事件触发线程触发回调,事件的回调放到队列中。若引擎线程空闲,依次执行队列中的任务。

    异步编程方式

    异步编程方式:发布/订阅(事件触发、回调函数)、promise(ES6)、async函数(ES6)。

    发布/订阅

    浏览器实现

    1.ajax

    通过ajax请求网络资源,网络资源返回以后,就会触发预先写好的回调函数,并且把回调函数任务放入执行队列中,当该回调函数在队列第一个并且js引擎执行队列时,该回调函数执行。

    var xmlhttp = new XMLHttpRequest();

    xmlhttp.open("get",url,true);

    xmlhttp.send(null);

    xmlhttp.onreadystatechange = callback;

    function callback(){

      if(xmlhttp.readySate == 4){

              if(xmlhttp.status == 200){

                   //...

             }

         }

    }

    2.setTimeout、setInterval

    setTimeout(callback,time)

    该方法的含义是time时间后把callback放入异步队列,当该函数在队列头,并且js引擎执行该队列时,此回调函数执行。

    setTimeout(function(){

      var a = 1;

    },0),

    0毫秒后把回调函数放入异步队列。

    3.addEventListener

    elem.addEventListener('click',callback,false);

    当点击elem元素时,把callback放到异步队列。

     

    开发者实现

    开发者自己通过发布/订阅 实现异步。

    var taskQueue = [];

    //订阅

    function subscribe(task){

      taskQueue.push(task);

    }

    //发布

    function publish(){

      for(var i = 0; i < taskQueue.length; i++){

        taskQueue[i]();

         }

    }

     

    subscribe(function(){console.log(1)});//订阅

    publish();//发布

     

    Promise

    Promise是ES6定义的规范,需要浏览器去实现。

    promise有3种状态:pendding, resolve, reject。

    每个promise某一时刻只能有其中的一种状态,pending为初始状态,并且pendding只能一次转向resolve或reject状态。

    可通过then方法注册将要执行的任务,这个任务什么时候执行,由promise的状态转换时间决定。

    var promise = new Promise(function(resolve,reject){

      setTimeout(resolve,100);//100ms后resolve放入异步队列

    });

    //promise由pending状态转为resolve状态,执行第2个参数的函数;

    //由pending状态转为reject状态,执行的第2个参数的函数

    promise.then(function resolve(){console.log(alert('resolve');},function reject(){alert('reject')});

     

    async函数

     async function print(value,time){

      await timeout(time);//异步任务 当这个任务完成后,才能继续向下执行

         console.log(value);

    }

    function timeout(time){

      new Promise(function(resolve){

              setTimeout(resolve,timeout);

         }));

    }

     

    print('hi',100);

    100ms后输出'hi'

      

    综上:

    (1)发布/订阅(事件触发、回调函数)、promise(ES6)思想是先注册一个任务,但不知道这个任务什么时候执行,需要合适的时间去执行这个任务。

    (2)async函数管理异步任务的上下执行顺序,异步执行完后,继续执行后面的代码。

  • 相关阅读:
    React源码解析-从头写一个React的难点与思路
    2017前端书籍推荐——如何一步步看懂框架源码
    React-ReactElement解析
    新手初学WPF本地化
    IOS 关闭键盘的几种方式
    专注技术
    test
    盒子模型
    CSS选择器详解(二)通用选择器和高级选择器
    CSS选择器详解(一)常用选择器
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/5627490.html
Copyright © 2020-2023  润新知