首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Javascript之async四重奏

异步操作一直以来都是JavaScript开发者十分热衷的话题,从callback、promise到generator再到async,其过程就像一首奏鸣曲,在向我们讲述着JavaScript异步操作的漫漫进程。

Callback hell的峥嵘岁月

Callback Hell

在ES6之前,我们更多的是采用callback的方式处理异步的过程,http://callbackhell.com/(如无法打开请复制链接至浏览器打开)中将类似于以下代码中诸多的’)}’这种形状称为’回调地狱’:

(注:左右/上下滑动屏幕可查看全部代码)

显然,没有人会保持足够的耐心去阅读这样的代码-----如果嵌套的层级更深的话.

怎么解决Callback Hell

我们先看如下的一段Javascript代码:

(注:左右/上下滑动屏幕可查看全部代码)

先读取example.txt中的关键字段,查询数据库后根据结果请求数据,其中包含的异步操作有:读取文件、查询数据库、请求数据,这种多层的嵌套显然是不利于阅读与维护的,为了解决这种问题,保持代码的结构整齐、浅显易读不失为一种好的方法:

(注:左右/上下滑动屏幕可查看全部代码)

对比之下,通过对函数进行命名或者是将函数封装成独立模块,代码会更具有阅读性。

Promise的应运而生

Promise

在JavaScript中,万物皆对象,Promise也不例外。我们可以将Promise当做一个包含异步操作并且可以被外界获取的对象,而这个对象有着自身的特点:

(1)内部状态不受外界影响

Promise中有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,只要产生这两种状态,就不会改变了,就被称为resolved。

在ES6中,Promise是一个构造函数,接收一个函数作为参数,这个函数的参数有两个:resolve和reject(也是函数类型)。resolve函数的作用是将Promise的状态从pending转变为resolved,异步操作成功时传递出异步操作的结果。reject函数的作用是将状态从pending转变为rejected,在异步操作失败时传出错误。

Promise的实例

通过Promise实现一个ajax:

(注:左右/上下滑动屏幕可查看全部代码)

虽然Promise有着非常灵活的使用方式,但是仍不免存在一些缺点:

无法中途取消Promise、无法直接从外部知道Promise抛出的错误、无法具体了解pending状态中的阶段。而Promise的这些问题却在我们的下一个话题---generator中得到了解决。

Generator的顺势而为

Generator函数

Generator函数是ES6提供的一种异步编程解决方案。

语法上,可以把它理解成:Generator函数是一个状态机,封装了多个内部状态。形式上,Generator函数是一个普通函数。整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器,异步操作需要暂停的地方,都用yield语句。

Generator函数有两个特征:

(1)function关键字和函数之间有一个星号(*),且内部使用yield表达式,定义不同的内部状态。

(2)调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。

执行Generator函数会返回一个遍历器对象,可以依次遍历函数内部的每一个状态。这个遍历器对象有三个方法:next()、throw()、return().

Next()

next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段:

(注:左右/上下滑动屏幕可查看全部代码)

上面代码中,第一个next()方法返回表达式x+2的值;第二个next()带有参数2,2作为上个阶段异步任务的返回结果,被函数内的y接收,再return出来。

Throw()

Throw方法用来补货函数体外抛出的错误:

(注:左右/上下滑动屏幕可查看全部代码)

Return()

Return方法用来终结遍历generator函数:

(注:左右/上下滑动屏幕可查看全部代码)

用于发送请求返回结果:

(注:左右/上下滑动屏幕可查看全部代码)

由此可见通过generator来控制异步的过程会更加简洁,而generator的yeild后面还可以是promise对象,因此在异步处理中相对会更灵活、更简便。

async-await的兼容并包

async函数

在es7中引入的async函数使得处理异步操作更加方便:

直接在普通函数前面加上async,表示这是一个异步函数,在要异步执行的语句前面加上await,表示后面的表达式需要等待。我们通过下面的例子先简单了解一下:

(注:左右/上下滑动屏幕可查看全部代码)

上面的代码指定50毫秒后输出hello world

async函数有多种使用方式:

//函数声明式:

async function foo() {}

//函数表达式:

const foo = async function () {}

//对象的方法:

let obj = }

obj.foo().then()

//箭头函数:

const foo = async () => {}

async函数返回一个promise对象,可以使用then方法添加回调函数:

(注:左右/上下滑动屏幕可查看全部代码)

Await命令后面是一个promise对象,如果不是会被转成一个立即resolve的promise对象:

(注:左右/上下滑动屏幕可查看全部代码)

通过async函数来发送多个请求:

(注:左右/上下滑动屏幕可查看全部代码)

由此可见,async函数不仅包含了promise、generator,代码也更加简洁。

总的来说,async函数就是generator函数的语法糖,相对于generator函数async函数有以下优点:

1.有内置执行器,不用调用next

Generator函数是需要调用next指令来运行异步的语句,async不需要调用next,直接像运行正常的函数那样运行就可以

2.有更好的语义化

语义化更明确,相比较于Generator的*和yield,async和await更明确。

3. await后面可以跟promise或者任意类型的值

yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

4.返回一个promise对象,可以调用.then

async直接返回一个promise对象,可以用then和catch来处理。

— END —

撰文★程金峰

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180620A1HSI600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券