JavaScript:期约

期约是一种机制。

期约通常表示一个正在处理的、尚未结束的操作,当相关操作结束时,期约会通知系统。

期约机制是 ECMAScript 实现异步编程的重要组成部分。

目前,流行的期约机制的规范是规范 Promises/A+ 。

ES6 规范以规范 Promises/A+ 为范本,来实现期约。

ES6 新增类型 Promise ,来实现(支持)规范 Promises/A+ 。


主要参考资料:

  • 《JavaScript 高级程序设计(第4版)》- P325(350/931)

期约状态机

期约主要有两个内部属性:

  • 期约的状态
  • 期约的值

期约的状态

期约有三种状态:

  • 待定(pending)
  • 兑现 / 履行(fulfilled)(也称作:解决(resolved))
  • 拒绝(rejected)

期约的状态代表期约是否完成:

  • 待定,表示期约尚未开始或者正在执行。
  • 解决,表示期约已成功完成。
  • 拒绝,表示期约没有成功完成。

期约的状态切换

期约的待定状态,被称为初始状态

期约的解决状态、拒绝状态,被称为落定(settled)状态

期约的状态可以从初始状态切换到落定状态,即:

  • 期约从待定状态切换到解决状态。
  • 期约从待定状态切换到拒绝状态。

期约从初始状态切换到落定状态的过程被称为落定

期约的落定是不可逆的,只要期约切换到落定状态,期约的状态就不会再发生改变。


期约的值

当期约转换为落定状态时,期约内部会生成一个值:

  • 当期约落定为解决时,这个值被简单地称为解决期约的值(value)
  • 当期约落定为拒绝时,这个值被称为拒绝期约的理由(reason)

期约的值的默认值为 undefined 。


创建期约实例

通过类型 Promise 的构造函数 Promise() ,创建期约实例。

期约的构造函数 Promise() :

  • 接收一个参数:
    函数,期约的执行函数,提供两个参数:

    1. resolve ,函数,使期约实例的状态落定为解决。
      接收一个参数(可选):任意值,作为解决期约实例的值。

    2. reject ,函数,使期约实例的状态落定为拒绝。
      接收一个参数(可选):任意值,作为拒绝期约实例的理由。

  • 返回值:
    期约。

关于执行函数:

在调用函数 resolve() 、reject() 之前,期约实例一直处于初始状态。

调用 resolve() 、reject() 中的任意一个,期约实例从初始状态切换为相应的落定状态。

期约实例处于落定状态后,再次调用 resolve() 、reject() 会静默失败,即期约实例落定后,不能改变期约实例的状态。

在执行函数中抛出错误,会导致构造函数 Promise() 返回一个拒绝期约,拒绝的理由为抛出的错误。

示例:

  • 创建一个待定期约实例

    const promise = new Promise(
    	() => {
         
    		console.log('Create a promise')
    	}
    )
    console.log(promise)
    
    // 输出:
    // Create a promise
    // Promise {<pending>}
    
  • 创建一个期约实例,1s 后期约实例落定为解决期约

    const promise_01 = new Promise(
    	(resolve) => {
         
    		setTimeout(
    			() => {
         
    				console.log(promise_01)
    				resolve()  // 落定为解决期约
    			},
    			1000
    		)
    	}
    )
    
    // 输出:
    // Promise {<fulfilled>: undefined}
    
  • 创建一个期约实例,1s 后期约实例落定为拒绝期约

    const promise_02 = new Promise(
    	(_, reject) => {
         
    		setTimeout(
    			() => {
         
    				reject()  // 落定为拒绝期约
    				console.log(promise_02)
    			},
    			1000
    		)
    	}
    )
    
    // 输出:
    // Promise {<rejected>: undefined}
    
  • 创建一个期约实例,1s 后期约实例随机落定,并指定落定期约的内部值。

    const promise_03 = new Promise(
    	(resolve, reject) => {
         
    		// 定义函数,对期约实例进行随机落定
    		function randomSettle() {
         
    			/**
    			* Math.random() ,返回区间为 [0, 1) 的随机数
    			* Math.round(x) ,对 x 进行四舍五入,并返回结果
    			*/
    			const handle = Math.round(Math.random())
    			switch(handle) {
         
    				case 0:
    					resolve('is resolved')  // 落定为解决期约,并指定解决期约的值
    					break
    				case 1:
    					reject('is rejected')  // 落定为拒绝期约,并指定拒绝期约的理由
    					break
    				default:
    					break
    			}
    		}
    		
    		setTimeout(
    			() => {
         
    				randomSettle()
    				console.log(promise_03)
    			},
    			1000
    		)
    	}
    )
    
    // 可能的输出_01:
    // Promise {<fulfilled>: is resolved}
    
    // 可能的输出_02:
    // Promise {<rejected>: is rejected}
    

创建解决期约实例

通过期约的静态函数 Promise.resolve() ,创建解决期约实例。

期约的静态函数 Promise.resolve() :

  • 功能:
    创建一个解决期约实例。

  • 接收一个参数(可选):
    任意值,作为解决期约实例的值。

  • 返回值:
    对象,解决期约实例。

示例:

  • 创建一个解决期约实例,并指定解决期约的值。
    const promise = Promise.resolve('is resolved')
    console.log(promise)
    
    // 输出:
    // Promise {<fulfilled>: 'is resolved'}
    

如果给静态函数 Promise.resolve() 传入的参数是一个期约,那么静态函数 Promise.resolve() 返回的是该期约自身。

示例:

  • 给静态函数 Promise.resolve() 传入一个期约。
    const promise = Promise.resolve('is resolved')
    const nestedPromise = Promise.resolve(promise)
    console.log(promise === nestedPromise)
    
    // 输出:
    // true
    

创建拒绝期约实例

通过期约的静态函数 Promise.reject() ,创建拒绝期约实例。

期约的静态函数 Promise.reject() :

  • 功能:
    创建一个拒绝期约实例。

  • 接收一个参数(可选):
    任意值,作为拒绝期约实例的理由。

  • 返回值:
    对象,拒绝期约实例。

示例:

  • 创建一个拒绝期约实例,并指定拒绝期约的理由。
    const promise = Promise.reject('is rejected')
    console.log(promise)
    
    // 输出:
    // Promise {<rejected>: 'is rejected'}
    

期约的落定

开发者只能在期约的执行函数中落定期约。

ES6 没有向开发者提供更多其它的可以动态改变期约状态的 API 。

期约状态的变化主要由 JavaScript 引擎根据 ES6 的期约规范进行控制。

ES6 没有向开发者提供可以获取期约状态的 API

期约状态只能通过函数 console.log() 打印出来。

即开发者不能通过原生 API 将期约的状态作为判断条件来进行开发,说明期约规范本身不建议(允许)开发者这样使用期约。


响应期约的落定

通过期约的原型方法 then() 、catch()、finally() ,向期约实例添加处理程序,可以响应期约实例的落定。

期约的原型方法 then() 、catch()、finally() 都会排期任务,即向消息队列添加任务。

所以通过期约的原型方法 then()、catch()、fianlly() 添加的处理程序,都会被排期到消息队列中,被异步执行。


前置知识

关于期约:

ES6 的期约实现了接口 Thenable 。

接口 Thenable :

  • 拥有一个方法:
    then(callback) ,用于指定后续执行的程序。
    示例:
    • 实现接口 Thenable 的对象。
    const obj = {
         
    	then(callback) {
         
    		// ...
    		callback('param')
    		// ...
    	}
    }
    

关于拒绝期约:

如果期约落定为拒绝期约,拒绝期约会抛出一个异步错误,异步错误的值为拒绝期约的理由。

拒绝期约抛出的异步错误,只能由该拒绝期约的拒绝处理程序捕获并处理。


Promise.prototype.then()

期约的原型方法 then() 用于为期约实例添加异步执行的解决处理程序、拒绝处理程序。

期约的原型方法 then() 会排期一个任务。

期约的原型方法 Promise.prototype.then() :

  • 功能:
    为期约实例添加异步执行的解决处理程序、拒绝处理程序。

  • 接收两个参数:

    1. 函数(可选)
      onResolved 处理程序(解决处理程序) ,在当前期约实例落定为解决期约后被调用。

      提供一个参数:value,当前期约实例落定为解决期约后的值。

    2. 函数(可选)
      onRejected 处理程序(拒绝处理程序) ,在当前期约实例落定为拒绝期约后被调用,捕获并处理当前期实例约落定为拒绝期约后抛出的异步错误。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值