千家信息网

javascript的异步操作方法是什么

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,这篇"javascript的异步操作方法是什么"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看
千家信息网最后更新 2024年09月22日javascript的异步操作方法是什么

这篇"javascript的异步操作方法是什么"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"javascript的异步操作方法是什么"文章吧。

javascript的异步操作方法有:1、回调函数;2、事件监听;3、"发布/订阅"模式;4、promise;5、generator;6、"async/await"。

本教程操作环境:windows7系统、javascript1.8.5版、Dell G3电脑。

什么是异步操作?

   异步模式并不难理解,比如任务A、B、C,执行A之后执行B,但是B是一个耗时的工作,所以,把B放在任务队列中,去执行C,然后B的一些I/O等返回结果之后,再去执行B,这就是异步操作。

JavaScript为什么需要异步操作?

  JavaScript语言的执行环境是"单线程", 所谓单线程,就是一次只能完成一件任务, 如果有多个任务就需要排队,一个完成了,继续下一个,这种方式在实现来说是非常简单的,但是如果一个任务耗时很长,那么后面的任务就需要排队等着,会拖延整个程序的执行。 常见的浏览器无响应假死)就是因为某一段JavaScript代码长时间运行(比如死循环),导致整个页面卡死,其他任务无法执行。

  为了解决这个问题,JavaScript语言将任务的执行模式分为两种:同步(Synchronous)和异步(Asynchronous)。

  同步任务执行的顺序和排队的顺序是一致的,而异步则需要有一个或者多个回调函数,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是等着前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的,异步的。

  异步模式非常重要,在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是ajax操作,在服务器端, 异步操作甚至是唯一方式,因为执行环境是单线程的,如果允许同步执行所有的http请求,服务器性能会急剧下降,很快就会失去响应。

JavaScript中异步操作的几种类型。

  JavaScript中异步编程的方法有:

  • 回调函数

  • 事件监听

  • 发布/订阅

  • promise

  • generator(ES6)

  • async/await (ES7)

  下面我来分别介绍这几种异步方法:

一、回调函数

  回调函数是异步编程中最基本的方法。假设有三个函数f1、f2、f3f2需要等待f1的执行结果,而f3是独立的,不需要f1和f2的结果,如果我们写成同步,就是这样的:

  f1();  f2();  f3();

  如果f1执行的很快,可以; 但是如果f1执行的很慢,那么f2和f3就会被阻塞,无法执行。这样的效率是非常低的。但是我们可以改写,将f2写成是f1的回调函数,如下:

  function f1(callback){    setTimeout(function () {      // f1的任务代码      callback();    }, 1000);  }

   那么这时候执行代码就是这样:

f1(f2);f3();

  这样,就是一个异步的执行了,即使f1很费时间,但是由于是异步的,那么f3()就会很快的得到执行,而不会受到f1和f2的影响。

  注意: 如果我们把f1写成这样呢?

function f1(callback){  // f1的任务代码  callback();}

  然后,我们同样可以这么调用:

f1(f2);f3()

  这时候还是异步的吗? 答案:不是异步。 这里的回调函数并非真正的回调函数,如果没有利用setTimeout含函数,那么f3()的执行同样需要等到f1(f2)完全执行完毕,这里要注意。而我们就是利用setTImeout才能做出真正的回调函数。

二、事件监听

  另一种异步的思路是采用事件驱动模式。任务的执行不取决于代码的顺序, 而取决于某个事件是否发生。 还是以f1、f2、f3为例子。 首先,为f1绑定一个事件(这里采用jquery的写法):

f1.on('done', f2);f3()

  这里的意思是: 当f1发生了done事件,就执行f2, 然后,我们对f1进行改写:

  function f1(){    setTimeout(function () {      // f1的任务代码            f1.trigger('done');    }, 1000);  }

  f1.trigger('done')表示, 执行完成后,立即触发done事件,从而开始执行f2。

  这种方法的优点就是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以去耦合,有利于实现模块化,缺点就是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

三、发布/订阅

  第二种方法的事件,实际上我们完全可以理解为"信号",即f1完成之后,触发了一个 'done',信号,然后再开始执行f2。

  我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"这个信号, 从而知道什么时候自己可以开始执行。 这个就叫做"发布/订阅模式", 又称为"观察者"模式 。

  这个模式有多种实现, 下面采用Ben Alman的Tiny PUb/Sub,这是jQuery的一个插件。

  首先,f2向"信号中心"jquery订阅"done"信号,

jQuery.subscribe("done", f2);

  然后,f1进行如下改写:

  function f1(){    setTimeout(function () {      // f1的任务代码            jQuery.publish("done");    }, 1000);  }

  jquery.pushlish("done")的意思是: f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。

  此外,f2完成执行后,也可以取消订阅(unsubscribe)。

 jQuery.unsubscribe("done", f2);

  这种方法的性质和"事件监听"非常类似,但是明显是优于前者的,因为我们可以通过查看"消息中心",了解到存在多少信号、每个信号有多少个订阅者,从而监控程序的运行。

四、promise对象

  promise是commonjs工作组提出来的一种规范,目的是为异步编程提供统一接口。

  简答的说,它的思想是每一个异步任务返回一个promise对象,该对象有一个then方法,允许指定回调函数。 比如,f1的回调函数f2,可以写成:

f1().then(f2);

  f1要进行下面的改写(这里使用jQuery的实现):

 function f1(){    var dfd = $.Deferred();    setTimeout(function () {      // f1的任务代码      dfd.resolve();    }, 500);    return dfd.promise;  }

  这样的优点在于,回调函数编程了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现很多强大的功能 。

  如:指定多个回调函数:

 f1().then(f2).then(f3);

  再比如,指定发生错误时的回调函数:

f1().then(f2).fail(f3);

  而且,他还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。 所以,你不用担心是否错过了某个事件或者信号,这种方法的确定就是编写和理解,都比较困难。 

五、generator函数的异步应用

   在ES6诞生之前,异步编程的方法,大致有下面四种:

  • 回调函数

  • 事件监听

  • 发布/订阅

  • promise对象

   没错,这就是上面讲得几种异步方法。 而generator函数将JavaScript异步编程带入了一个全新的阶段!

   比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。

   相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着

协程

  传统的编程语言中,早就有了异步编程的解决方案,其中一种叫做协程,意思是多个线程互相协作,完成异步任务

  协程优点像函数,又有点像线程,运行流程如下:

  • 第一步,协程A开始执行。

  • 第二步,协程A执行到一半,进入暂停执行权转移到协程B

  • 第三步,(一段时间后)协程B交还执行权

  • 第四步,协程A恢复执行

  上面的协程A,就是异步任务,因为它分为两段(或者多段)执行。

  举例来说,读取文件的协程写法如下:

function *asyncJob() {  // ...其他代码  var f = yield readFile(fileA);  // ...其他代码}

  上面代码的函数asyncJob是一个协程,奥妙就在于yield命令, 它表示执行到此处,执行权交给其他协程,也就是说yield命令是异步两个阶段的分界线。

  协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续向后执行,它的最大优点就是代码的写法非常像同步操作,如果去除yield命令,简直是一模一样。

协程的Generator函数实现

  Generator函数是协程在ES6中的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

  整个Generator函数就是一个封装的异步任务,或者说异步任务的容器。 异步任务需要暂停的地方,都用yield语句注明。 如下:

function* gen(x) {  var y = yield x + 2;  return y;}var g = gen(1);g.next() // { value: 3, done: false }g.next() // { value: undefined, done: true }

  在调用gen函数时 gen(1), 会返回一个内部指针(即遍历器)g。 这是Generator函数不同于普通函数的另一个地方,即执行它(调用函数)不会返回结果, 返回的一个指针对象 。调用指针g的next方法,会移动内部指针(即执行异步任务的第一阶段),指向第一个遇到的yield语句,这里我们是x + 2,但是实际上这里只是举例,实际上 x + 2 这句应该是一个异步操作,比如ajax请求。 换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。 value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段。

Generator函数的数据交换和错误处理

  Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。

  next返回值的value属性,是 Generator 函数向外输出数据;next方法还可以接受参数,向 Generator 函数体内输入数据。

function* gen(x){  var y = yield x + 2;  return y;}var g = gen(1);g.next() // { value: 3, done: false }g.next(2) // { value: 2, done: true }

  上面代码中,第一next方法的value属性,返回表达式x + 2的值3。第二个next方法带有参数2,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量y接收。因此,这一步的value属性,返回的就是2(变量y的值)。

Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。

function* gen(x){  try {    var y = yield x + 2;  } catch (e){    console.log(e);  }  return y;}var g = gen(1);g.next();g.throw('出错了');// 出错了

上面代码的最后一行,Generator 函数体外,使用指针对象的throw方法抛出的错误,可以被函数体内的try...catch代码块捕获。这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。

异步任务的封装

  下面看看如何使用 Generator 函数,执行一个真实的异步任务。

var fetch = require('node-fetch');function* gen(){  var url = 'https://api.github.com/users/github';  var result = yield fetch(url);  console.log(result.bio);}

  上面代码中,Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从 JSON 格式的数据解析信息。就像前面说过的,这段代码非常像同步操作,除了加上了yield命令。

  执行这段代码的方法如下。

var g = gen();var result = g.next();result.value.then(function(data){  return data.json();}).then(function(data){  g.next(data);});

  上面代码中,首先执行 Generator 函数,获取遍历器对象,然后使用next方法(第二行),执行异步任务的第一阶段。由于Fetch模块返回的是一个 Promise 对象,因此要用then方法调用下一个next方法。

  可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。

  如下:

function* gen(x) {  yield 1;  yield 2;  yield 3;  return 4;}var a = gen();console.log(a.next());console.log(a.next());console.log(a.next());console.log(a.next());

  最终,打印台输出

即开始调用gen(),并没有真正的调用,而是返回了一个生成器对象,a.next()的时候,执行第一个yield,并立刻暂停执行,交出了控制权; 接着,我们就可以去a.next() 开始恢复执行。。。 如此循环往复。  

每当调用生成器对象的next的方法时,就会运行到下一个yield表达式。 之所以称这里的gen()为生成器函数,是因为区别如下:

  • 普通函数使用function来声明,而生成器函数使用 function * 来声明

  • 普通函数使用return来返回值,而生成器函数使用yield来返回值。

  • 普通函数式run to completion模式 ,即一直运行到末尾; 而生成器函数式 run-pause-run 模式, 函数可以在执行过程中暂停一次或者多次。并且暂停期间允许其他代码执行。

async/await

  async函数基于Generator又做了几点改进:

  • 内置执行器,将Generator函数和自动执行器进一步包装。

  • 语义更清楚,async表示函数中有异步操作,await表示等待着紧跟在后边的表达式的结果。

  • 适用性更广泛,await后面可以跟promise对象和原始类型的值(Generator中不支持)

  很多人都认为这是异步编程的终极解决方案,由此评价就可知道该方法有多优秀了。它基于Promise使用async/await来优化then链的调用,其实也是Generator函数的语法糖。 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

  await得到的就是返回值,其内部已经执行promise中resolve方法,然后将结果返回。使用async/await的方式写回调任务:

async function dolt(){    console.time('dolt');    const time1=300;    const time2=await step1(time1);    const time3=await step2(time2);    const result=await step3(time3);    console.log(`result is ${result}`);    console.timeEnd('dolt');}dolt();

  可以看到,在使用await关键字所在的函数一定要是async关键字修饰的。

  功能还很新,属于ES7的语法,但使用Babel插件可以很好的转义。另外await只能用在async函数中,否则会报错

以上就是关于"javascript的异步操作方法是什么"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

函数 任务 方法 代码 就是 事件 信号 对象 阶段 编程 模式 订阅 结果 属性 程序 错误 同步 多个 文件 处理 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全课程设计实验心得 服务器安装数据库是要干嘛的 滁州游戏服务器诚信企业推荐 数据库清空一个表可以用哪个操作 1.9粘土服务器 一禅智能连接服务器失败 手机数据库查看软件 web遥感软件开发 服务器有网络安全模式吗 广德先进软件开发服务品质保障 数据库防伪技术服务至上 自建机房服务器配置清单 小米数据库开发好吗 数据库系统 界面 软件开发行业基本工资和绩效工资 南京网络安全与执法招聘 软件开发项目需求确认表 nis管理多git服务器账户 如何查询数据库中乱码的数据 数据库系统的特点是什么数据独立 网络安全 大事件 社会信用信息基础数据库 网络安全的具体要求是什么 广东常用软件开发价格表 系统集成软件开发运维监理 广东曙光服务器维修技术虚拟主机 数据库系统 界面 mysql数据库的笔试题 浅析计算机网络安全pdf下载 物理服务器网卡
0