期约与异步函数

585 阅读4分钟

ECMAScript6新增了Promise(期约)引用类型

同步行为对应内存中顺序执行的处理器指令。

异步行为类似于系统中断,即当前进程外部的实体可以出发代码执行。

  • 期约是对尚不存在的结果的一个替身。
  • 可以通过new操作符来实例化。创建新期约时需要传入执行器(executor)函数作为参数。下面的额例子使用一个空函数对象来应付一下解释器。
let p = new Promise(()=>{})
setTimeout(console.log, 0 , p);

之所以说是应付解释器,是因为如果不提供执行器函数,就会抛出SyntaxError。

  • 期约是一个有状态的对象

期约状态机

  1. 期约可能处于3种状态之一:待定pending、兑现fulfilled有时也成为解决resolved、拒绝rejected
  2. 待定是期约的最初始状态。在待定状态下,期约可以落定为代表成功的兑现状态,或者代表失败的拒绝状态。
  3. 只要从待定转换为兑换或者拒绝状态,都是不可逆的。状态不再改变。
  4. 不能保证期约必然会脱离待定状态。所以无论期约是哪种状态都要有恰当的行为。
  5. 期约的状态是私有的,不能直接通过javascript检测到,主要是为了避免根据读取到的期约状态,以同步方式处理期约对象。
  6. 期约的状态也不能被外部javascript代码修改,这与不能读取该状态的原因一样:期约故意将异步行为封装起来,从而隔离外部的同步代码。

期约的两大用途

  1. 这个状态机就是期约可以提供的最有用的信息
  2. 期约封装的异步操作会实际生成某个值,而程序期待状态改变时可以访问这个值。

通过执行函数控制期约函数

由于期约状态是私有的,所以只能在内部执行,内部操作在期约的执行器函数中完成,执行器主要有两项职责:初始化期约的异步行为和控制状态的最终转换。

  • 控制期约状态的转换是通过调用他的两个函数参数实现的。这两个函数参数通常都命名为resolve()和reject()
  • 调用resolve()把状态切换为兑现,调用reject()切换为拒绝。
let p1 = new Promise((resolve,reject)=>resolved());
setTimeout(console.log,0,p1)//Promise <resolved>
let p2 = new Promise((resolve,reject)=>reject());
setTimeout(console.log,0,p2)//Promise <rejected>
//Uncaught error(in promise)

在上面的例子中,没有异步操作,因为在初始化期约时,执行器函数已经改变了每个期约的状态,这里的关键在于,执行器函数是同步执行的。这里因为执行器函数是期约的初始化程序。通过下面的例子可以看出上面代码的执行顺序。

new Promise(()=>setTimeout(console.log,0,'executor'));
setTimeout(console.log,0,'promise initalized');
//executor
//promise initialized

添加setTimeout可以推迟切换状态: let p = new Promise((resolve,reject)=>setTimeout(resolve,1000))

Promise.resolve()静态方法可以实例化一个解决的期约

这个解决期约的值对应着方法的第一个参数。多余的参数会忽略。

Promise.reject()静态方法可以实例化一个拒绝的期约并抛出一个异步错误。

这个错误不能通过try/catch来捕获,只能通过拒绝处理程序捕获。

期约的实例方法

期约实例的方法是连接外部同步代码与内部异步代码之间的桥梁。这些方法可以访问异步操作返回的数据,处理期约成功和失败的输出,连续对期约求值,或者添加只有期约进入终止状态时才会执行的代码。

一、实现Thenable接口

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

二、Promise.prototype.then()

是为期约实例添加处理程序的主要方法。这个then()方法接收最多两个参数:onResolved处理程序和onRejected处理程序。这两个参数都是可选的。

二、Promise.prototype.catch()

用于给期约添加拒绝处理程序,这个方法只接收一个参数,onRejected处理程序。事实上这就是一个语法糖,相当于调用Promise.prototype.then(null,onRejected)

二、Promise.prototype.finally()

方法用于给期约添加onFinally处理程序,这个处理程序在期约转换为解决或者拒绝状态时都会执行,单onFinally没有办法知道期约的状态是解决还是拒绝,所以这个方法主要用于添加清理代码。