侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

ES6的异步终极解决方案分享

前端  /  管理员 发布于 4年前   396

前言

Promise async generator是ES6之后才被提出来的,他们都能够用来解决以前JS异步调用产生的一系列问题,例如大名鼎鼎的回调地狱!!!

什么是回调地狱?

在以前js中,我们是无法知晓一个异步操作是否执行完成,为了在异步操作完成后执行特定的代码,我们需要传入回调函数,请看下面的栗子:

这是一个简单的例子,在请求完成后(可以理解为异步操作)执行特定的代码

 //我们需要在请求完成后输出请求完成,请看回调法 function show(params) { request('这是请求参数', () => { console.log('请求完成') }) } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data, callBack) { //下面的定时器模拟请求时间 setTimeout(data => { callBack(data); }, 3000); } show()

一次回调当然简单,如果是在这次请求完成后需要立即发起下一次请求呢?例如需要请求request10次,必须在上次请求完成后才能进行下一次请求,来看看 回调地狱 是怎么样的

 //我们需要在请求完成后输出请求完成,请看回调法 function show(params) { request('这是请求参数', () => { console.log('请求完成1次') request('这是请求参数', () => { console.log('请求完成2次') request('这是请求参数', () => {  console.log('请求完成3次')  request('这是请求参数', () => {  console.log('请求完成4次')  request('这是请求参数', () => {  console.log('请求完成5次')  //这才第五次.....  })  }) }) }) }) } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data, callBack) { //下面的定时器模拟请求时间 setTimeout(data => { callBack(data); },1000); } show()

这才第5次回调,但是代码的可读性已经极差了!

让我们先看看 Promise async generator怎么解决这个问题,后面再说其使用方式

首先 Promise 篇

 //我们需要在请求完成后输出请求完成,请看回调法 function show(params) { request('这是请求参数').then( resolve => { console.log('请求完成1次'); return request('这是请求参数') } ).then( resolve => { console.log('请求完成2次'); return request('这是请求参数') } ).then( resolve => { console.log('请求完成3次'); return request('这是请求参数') } ).then( resolve => { console.log('请求完成4次'); return request('这是请求参数') } ).then( resolve => { console.log('请求完成5次'); return request('这是请求参数') } ) } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data) { return new Promise( resolve => { //下面的定时器模拟请求时间 setTimeout(data => {  resolve(data) }, 1000); } ) } show()

虽然还是很长,但是至少嵌套很少了,可读性也比之前更高

再来看看 async

切记,async必须和Promise配合使用

 //我们需要在请求完成后输出请求完成,请看回调法 async function show(params) { let result = await request('这是请求参数') console.log('请求完成1次'); result = await request('这是请求参数') console.log('请求完成2次'); result = await request('这是请求参数') console.log('请求完成3次'); result = await request('这是请求参数') console.log('请求完成4次'); result = await request('这是请求参数') console.log('请求完成5次'); } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data) { return new Promise( resolve => { //下面的定时器模拟请求时间 setTimeout(data => {  resolve(data) }, 1000); } ) } show()

代码是不是更加简短了?而且看起来和同步一样,事实上,这就是使用同步的方式写异步代码,这代码也是同步执行的

最后看看 generator

 //我们需要在请求完成后输出请求完成,请看回调法 function* show() { let a1 = yield request('请求参数', () => { console.log('这是第1次调用'); }); let a2 = yield request('请求参数', () => { console.log('这是第2次调用'); }); let a3 = yield request('请求参数', () => { console.log('这是第3次调用'); }); let a4 = yield request('请求参数', () => { console.log('这是第4次调用'); }); let a5 = yield request('请求参数', () => { console.log('这是第5次调用'); }); } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data, callBack) { //下面的定时器模拟请求时间 setTimeout(() => { callBack(data) }, 1000); } let a = show() a.next(); a.next(); a.next(); a.next(); a.next();

以上是异步编程的ES6解决方案,接下来让我们把这3种方式都详细了解下

一.Promise

关于Promise的一些原型,函数,请移步 官方链接

Promise的中文名,也就是承诺,保证,

大家可以将Promise理解为JS的一个承诺,也就是对异步操作的一个承诺,咱先不管异步操作是否能够执行成功,使用Promise的所有异步操作,JS已经承诺处理了,咱就通过Promise的状态来知晓异步操作的执行结果。

一个 Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 表示着操作完成,状态成功。
  • rejected: 意味着操作失败。

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用

上文提到Promise的原型中的函数then,then可以接收2个参数(resolve [,reject])

第一个参数resolve 是对成功的一个处理,类型为Function。你可以在其中做 一些异步成功后的操作

第二个参数reject是对失败的一个处理,类型为Function。你可以在其中做 一些异步失败后的操作

一般情况下then我们只会传一个参数,也就是默认的成功处理,失败处理会使用 catch函数

catch函数只有一个参数,也就是代表失败的reject

来看看then catch的使用案例

 function show(params) { //正常的请求成功操作 request('这是请求参数').then( resolve => { console.log(resolve); } ) //在then里面同时做成功和失败的操作 request('这是请求参数').then( resolve => { //这儿是成功 console.log(resolve); }, reject => { //这儿是失败 console.log(reject); } ) //正常的请求失败操作 request('这是请求参数').catch( reject => { console.log(reject); } ) } /** * 模拟发起一个http请求 * @param {object} data 请求的参数 * @param {function} callBack 回调函数 */ function request(data) { return new Promise( (resolve, reject) => { //下面的定时器模拟请求时间 setTimeout(data => {  // resolve('请求成功')  reject('请求失败') }, 1000); } ) } show()

使用 Promise 能够使你的异步操作变得更加优雅,可读性也比较高,同时,Promise在ES6的各大插件中也使用的相当广泛,所以掌握 Promise是非常有必要的

二.async / await

想详细了解更多,请移步官方文档

async关键字用来定义一个function,用来标识此函数是一个异步函数

切记 切记 切记, await 关键字仅仅在 async function中有效。如果在 async function函数体外使用 await ,你只会得到一个语法错误SyntaxError

async关键字放在函数的声明之前,例如:

 async function test() { } async () => { } async data => { } class Test { async show() {  } }

无论是普通的函数,Lambda表达式,或是ES6的class,该关键字都是放置在函数的声明之前

在调用声明了async的函数时,会返回一个Promise对象

而await关键字则是放置在异步操作的调用之前,await会使得async函数在执行到异步操作时暂停代码执行,直到异步操作返回的Promise状态更改为 fulfilled 或 rejected,此时代码会继续执行,并自动解析Promise返回的值,无论是成功还是失败,都会解析到await关键字前面定义接收的参数,请看例子:

class Test { /** * 线程休眠 * @param {number} timer 休眠毫秒数 */ Sleep(timer) { return new Promise( resolve => { setTimeout(() => {  resolve(timer) }, timer); } ) }}let T = new Test();async function show() { console.log('第一次'); await T.Sleep(1000) console.log('第二次');}show()

上面的实例调用show函数,会立即打印出 第一次,延时1000毫秒后,会打印出 第二次

原理嘛,就是模仿Java的线程休眠函数Sleep

在打印了 第一次 后,会调用T的Sleep函数,Sleep函数返回了一个Promise,在定时器执行完毕后调用Promise的resolve函数,会将Promise 的状态更改为 fulfilled,此时await检测到Promise的状态更改,继续执行代码,输出 第二次

三 . generator

generator(生成器)是ES6标准引入的新的数据类型,使用方式是在函数名前加上*,generator和普通的函数差不多,但是可以返回多次。

嗯,所有的函数都有默认的返回值,如果没有明确定义,那就会返回undefined,generator也不例外!

generator使用yield关键字来中途返回值,请看案例

 function* a(num) { let sum = yield num + 1 console.log(sum);//2 此处是next(r.value)传入的值 let sum2 = yield sum + 2 } let result = a(1); let r = result.next() console.log(r);//此处返回第一次yield的值 2 console.log(result.next(2));//此处返回第二次yield的值 4 console.log(result.next());//此处并没有第三次yield


第一次输出的是第一次yield的值,此时num为调用a函数时传入的值 1,yield返回了num+1,所以第一行打印的对象 value值是 2

第二次打印的是sum值,也就是第一个yield关键字前面接收的值,此值是下面result.next(2)传入的 ,next函数传入的参数,会赋值到相应的yield关键字左边接收的那个变量,在上方案例,也就是sum变量

第三次输出的是a函数中第二个yield返回的值,此时sum为第一次next(2)传入的2,所以此次返回的值是2+2,所以值也就是 4

第四次输出的是最后一个next(),但是上方generator并没有相应的yield返回,所以此时的value为undefined

yield返回的值是一个对象,其中有done和value两个属性,

  • done 表示该generator是否执行完毕,当没有yield返回时,done的值为true,也就是代表当前generator执行完毕
  • value表示此次yield关键字右方表达式返回的值,当没有yield时,value为undefined

generator是支持迭代器操作的,例:

 function* a(num) { let sum = yield num + 1 console.log(sum);//2 此处是next(r.value)传入的值 let sum2 = yield sum + 2 } let result = a(1); for (const key of result) { console.log(key);  }


事实证明generator是实现了迭代器的接口的!

嗯,关于generator的实际应用场景,我是没有遇见的,不过听说 async/await是generator的语法糖??

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家的支持。

您可能感兴趣的文章:

  • 详解ES6之async+await 同步/异步方案
  • 微信小程序 es6-promise.js封装请求与处理异步进程
  • ES6记录异步函数的执行时间详解
  • ES6 javascript的异步操作实例详解
  • 详解ES6中的三种异步解决方案
  • ES6中Generator与异步操作实例分析
  • ES6的Fetch异步请求的实现方法
  • JS基于ES6新特性async await进行异步处理操作示例
  • 详解ES6 系列之异步处理实战
  • ES6中的迭代器、Generator函数及Generator函数的异步操作方法


  • 上一条:
    如何给element添加一个抽屉组件的方法步骤
    下一条:
    elementUI 动态生成几行几列的方法示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客