js 异步实现与编程

来源:互联网 发布:java jsoup 爬虫实例 编辑:程序博客网 时间:2024/06/07 07:03

同步

同步

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

如下图所示,任务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函数管理异步任务的上下执行顺序,异步执行完后,继续执行后面的代码。


原创粉丝点击