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


Posted in Javascript onJuly 11, 2019

前言

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

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

第一次输出的是第一次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);
 
 }

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

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

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

总结

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

Javascript 相关文章推荐
动态调用css文件——jquery的应用
Feb 20 Javascript
jQuery实现自定义checkbox和radio样式
Jul 13 Javascript
jquery实现初次打开有动画效果的网页TAB切换代码
Sep 06 Javascript
JavaScript字符串检索字符的方法
Jun 23 Javascript
Angular中自定义Debounce Click指令防止重复点击
Jul 26 Javascript
seajs中模块依赖的加载处理实例分析
Oct 10 Javascript
jQuery zTree 异步加载添加子节点重复问题
Nov 29 jQuery
JS实现在文本指定位置插入内容的简单示例
Dec 22 Javascript
JS实现可视化文件上传
Sep 08 Javascript
layer父页获取弹出层输入框里面的值方法
Sep 02 Javascript
layui table动态表头 改变表格头部 重新加载表格的方法
Sep 21 Javascript
原生js实现自定义滚动条
Jan 20 Javascript
微信小程序的开发范式BeautyWe.js入门详解
Jul 10 #Javascript
实现一个 Vue 吸顶锚点组件方法
Jul 10 #Javascript
vue webpack重写cookie路径的方法
Jul 10 #Javascript
vue登录页面cookie的使用及页面跳转代码
Jul 10 #Javascript
Laravel admin实现消息提醒、播放音频功能
Jul 10 #Javascript
微信小程序把百度地图坐标转换成腾讯地图坐标过程详解
Jul 10 #Javascript
JavaScript实现的弹出遮罩层特效经典示例【基于jQuery】
Jul 10 #jQuery
You might like
Windows下的PHP5.0详解
2006/11/18 PHP
phpword插件导出word文件时中文乱码问题处理方案
2014/08/19 PHP
yii2 页面底部加载css和js的技巧
2016/04/21 PHP
PHP实现导出带样式的Excel
2016/08/28 PHP
Expandable "Detail" Table Rows
2007/08/29 Javascript
javascript打开新窗口同时关闭旧窗口
2009/01/16 Javascript
JavaScript 新手24条实用建议[TUTS+]
2009/06/21 Javascript
javascript模拟select,jselect的方法实现
2012/11/08 Javascript
HTML页面登录时的JS验证方法
2014/05/28 Javascript
使用jquery实现放大镜效果
2014/09/02 Javascript
javascript获取dom的下一个节点方法
2014/09/05 Javascript
jquery实现手机号码选号的方法
2015/07/31 Javascript
JavaScript编写简单的计算器
2015/11/25 Javascript
AngularJS基础 ng-repeat 指令简单示例
2016/08/03 Javascript
jquery 正整数数字校验正则表达式
2017/01/10 Javascript
详谈JS中数组的迭代方法和归并方法
2017/08/11 Javascript
Angular8引入百度Echarts进行图表分析的实现代码
2019/11/27 Javascript
[06:07]DOTA2-DPC中国联赛3月5日Recap集锦
2021/03/11 DOTA
在Python中用split()方法分割字符串的使用介绍
2015/05/20 Python
Python制作Windows系统服务
2017/03/25 Python
Linux下多个Python版本安装教程
2018/08/15 Python
Python利用ORM控制MongoDB(MongoEngine)的步骤全纪录
2018/09/13 Python
浅述python2与python3的简单区别
2018/09/19 Python
python通过ffmgep从视频中抽帧的方法
2018/12/05 Python
python判断单向链表是否包括环,若包含则计算环入口的节点实例分析
2019/10/23 Python
Python可变对象与不可变对象原理解析
2020/02/25 Python
selenium WebDriverWait类等待机制的实现
2020/03/18 Python
梅西酒窖:Macy’s Wine Cellar
2018/01/07 全球购物
美国杰西潘尼官网:JCPenney
2019/06/12 全球购物
2014年党的群众路线教育实践活动整改措施(个人版)
2014/09/25 职场文书
家属慰问信
2015/02/14 职场文书
综合办公室岗位职责
2015/04/11 职场文书
傲慢与偏见读书笔记
2015/06/29 职场文书
2015中学学校工作总结
2015/07/20 职场文书
2019年农民幸福观调查的实践感悟
2019/12/19 职场文书
create-react-app开发常用配置教程
2022/06/25 Javascript