十一、期约与异步函数

 

一、异步编程

1.异步和同步

同步行为对应内存中顺序执行的处理器指令;异步行为类似于系统中断,即当前进程外部的实体可以触发代码执行

2.以往的异步编程模式

早期的js中,只支持定义回调函数来表明异步操作的完成。串联多个异步操作是一个常见的问题,通常需要深度嵌套回调函数(回调地狱)

2-1)异步返回值

广泛接受的一个策略是给异步操作一个回调,回调中包含要使用异步返回值的代码

2-2)失败处理

添加成功回调和失败回调

2-3)嵌套异步回调

异步返回值又依赖另一个异步返回值

二、期约

期约是对一个尚不存在的结果的一个替身

1.Promises/A++规范

早期的期约机制在jQuery和Dojo中以Deferred API等形式出现;

2010年,CommonJS项目实现的Promises/A规范日益流行;

2012年,Promises/A+组织分叉了CommonJS的Promises/A建议,制定了Promises/A+规范;

ES6增加了对Promises/A+规范的完善支持,即Promise类型

2.期约基础

2-1)期约状态机

期约可能处于pending、 fulfilled和rejected三种状态之一,且从pending落定为其他任意一种状态的过程都不可逆

期约的状态是私有的不能直接通过JS检测到;也不能被外部JS代码修改(故意将异步行为封装起来,从而隔离外部同步代码)

2-2)解决值、拒绝理由及期约用例

期约有两大用途:a)抽象地表示一个异步操作;b)另外一些情况,期约封装的异步操作会实际生成某个值,程序期待期约状态变化时可以访问这个值。

2-3)通过执行函数控制期约状态

内部操作在期约的执行器函数中完成,执行器函数的两个职责:a)初始化期约的异步行为;b)控制状态的最终转化

执行器函数是同步执行的

为避免期约卡在待定状态,可以添加一个定时退出功能

2-4)Promise.resolve()

可以实例化一个状态为fulfilled的期约;这个方法是幂等的

2-5)Promise.reject()

会实例化一个拒绝的期约,并抛出一个异步错误(这个错误不能被try/catch捕获,只能通过拒绝处理程序捕获);

这个函数不是幂等的

2-6)同步/异步执行的二元性

期约的异步特性:他们是同步对象(在同步模式中使用),但也是异步执行模式的媒介

代码一旦开始以异步模式执行,则唯一与之交互的方法就是使用异步结构----期约的方法

3.期约的实例方法

连接外部同步代码和内部异步代码之间的桥梁

3-1)实现Thenable接口

ECMAScript暴露的异步结构中,任何对象都有一个then方法

3-2)Promise.prototype.then()

接受两个可选参数,onResolved处理程序和onRejected处理程序,如果只想传onRejected处理函数,则需要在onResolved传入undefined

3-3)Promise.prototype.catch()

用于给期约添加拒绝处理程序,只接受一个参数,onRejected处理程序

3-4)Promise.protototype.finally()

用于给期约添加onFinally处理程序,无论状态转为fulfilled和rejected,都会执行

3-5)非重入期约方法

“非重入特性”:

当期约进入落定状态时,与该状态相关的处理程序只是会排期,而非立即执行。跟在处理程序之后的同步代码一定会在处理程序之前执行。

非重入特性适用于onResolved/onRejected/catch/finally处理程序

3-6)邻近处理程序的执行顺序

如果给期约添加了多个处理程序,当状态变化时,相关处理程序会依次执行,无论是then/catch/finally

3-7)传递解决值和拒绝理由

分别作为resolve/reject的第一个参数往后传,这些值会传给各自的处理程序,作为onResolved/onRejected的唯一参数

3-8)拒绝期约与拒绝错误处理

在期约中抛出错误时,实际是从消息队列中异步抛出的,并不会组织运行时继续执行同步命令

4、期约连锁与期约合成

4-1)期约连锁:

把期约逐个逐个串联起来,因为每个期约都有自己的then/catch/finally方法,这些方法都会返回一个新的期约,而新的期约又有自己的实例方法。

这种连缀的方法调用就可以构成所谓的“期约连锁”

4-2)期约图:期约连锁可以构建有向非循环图的结构

4-3)Promise.all()Promise.race()

Promise.all()接受一个可迭代对象作为参数,返回一个新的期约。可迭代对象中的元素会通过Promise.resolve()转为期约

合成的期约会在所有的期约都解决后解决

如果有期约拒绝,则会将第一个拒绝的拒绝理由作为理由返回

Promise.race():无论是拒绝还是解决,只要第一个落定就会包装其解决值或者拒绝理由并返回

5.期约扩展

很多第三方库实现中具备而ECMAScript规范中却未涉及的两个特性:期约取消和进度追踪

(太妙了,见于红皮书P346)

期约取消:用Kevin Smith提到的“取消令牌”,生成的令牌实例提供一个接口,利用这个接口,可以取消期约;同时提供一个期约实例,用于触发取消后的操作并求值取消状态

期约进度通知:扩展Promise类,实现notify()方法

三、异步函数

ES8新增async/await,旨在利用异步结构组织代码的问题

1.异步函数async/await

1-1).async 声明异步函数,可以用在函数声明、函数表达式、箭头函数和方法上

异步函数如果使用return返回了值,则这个值会被Promise.resolve()包装

异步函数的返回值期待一个实现thenable接口的对象,如果是则可以交给then()处理程序解包;如果不是,则用Promise.resolve()包装

如果在异步函数中抛出错误,会返回拒绝的期约

1-2).await 可以暂停异步函数代码的执行,等待期约解决

await期待一个实现了thenable接口的对象,如果是,则由await解包;如果不是,就被当做已解决的期约

抛出同步的错误,则会返回拒绝的期约

1-3).await的限制

await必须在异步函数中使用;

定义并立即执行异步函数是没问题的;

2.停止和恢复执行

JavaScript运行时碰到了await关键字时,会记录在哪暂停执行。等到await右边的值可用,JavaScript运行时会向消息队列推送一个任务,这个任务会恢复异步函数的执行

3.异步函数策略

3-1)实现sleep(), 在程序中加入非阻塞的暂停

3-2)利用平行执行

如果顺序不是必须保证的,可以先一次性初始化所有期约,然后分别等待结果。
虽然期约不是顺序执行的,但是await按顺序接受了结果

3-3)串行执行期约

3-4)栈追踪与内存管理

JavaScript引擎在创建期约时,会尽可能保存完整的调用栈。异步函数比期约节省内存。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘风xs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值