JavaScript:期约

本文详细介绍了JavaScript中的期约(Promise)机制,包括期约的状态机、创建期约实例、期约的落定、响应期约的落定、拒绝期约以及期约的连锁和合成。期约是ECMAScript实现异步编程的核心,通过Promise构造函数、Promise.resolve()和Promise.reject()等方法创建和管理期约。通过then()、catch()和finally()方法响应期约的落定,实现异步任务的顺序执行和错误处理。此外,还讲解了Promise.all()和Promise.race()的用法,用于组合多个期约。

期约是一种机制。

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

期约机制是 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 处理程序(拒绝处理程序) ,在当前期约实例落定为拒绝期约后被调用,捕获并处理当前期实例约落定为拒绝期约后抛出的异步错误。

      提供一个参数:reason,当前期约实例落定为拒绝期约后的理由。

  • 返回值:
    期约,期约的状态会动态改变:

    • 初始状态

      在排期的任务执行完毕之前,期约为待定期约。

    • 落定状态

      在排期的任务执行完毕之后,期约落定。

      可能的落定状态:

      • 继承当前期约实例的状态

      情形:

      • 没有提供处理程序

      期约会继承当前期约实例的状态,并且继承当前期约实例的值。

      • 解决

      情形:

      • 提供处理程序。

      解决期约的值为处理程序的返回值。

      如果处理程序没有返回值,则解决期约的值为 undefined 。

      • 拒绝

        情形:

        • 在处理程序中抛出错误。
        • 处理程序返回一个拒绝期约(即抛出异步错误)。

        拒绝期约的理由为:

        • 处理程序抛出的错误信息。
        • 处理程序返回的拒绝期约的理由。
    • 保持待定状态

      情形:

      • 处理程序返回一个待定期约。

      切换到落定状态:

      • 直到处理程序返回的待定期约落定后,期约才会根据返回期约的落定状态进行落定。

如果想要不提供解决处理程序,只提供拒绝处理程序,那么就在期约的原型方法 then() 的第一个参数上传入 null 。

这样有助于避免在内存中创建多余的对象,对期待函数参数的原型方法 then() 也是一个交代。

示例:

  • 向期约实例添加解决处理程序。

    // 创建解决期约
    const promise = new Promise(
    	(resolve, reject) => {
    		setTimeout(
    			() => {
    				resolve(1)
    				console.log('promise is resolved: ', promise)
    			},
    			1000
    		)
    	}
    )
    
    // 打印此时的期约 promise 
    console.log('promise is pending: ', promise)
    
    // 向期约 promise 添加解决处理程序,并接收返回的期约实例
    const nextPromise = promise.then(
    	(value) => {
    		console.log('value of promise when resolved: ', value )  // 打印期约 promise 落定为解决期约后的值
    		return 2
    	}  // 解决处理程序
    ) 
    
    // 向期约 promise 添加解决处理程序,打印期约 nextPromise
    promise.then(
    	() => {
    		console.log('nextPromise: ', nextPromise)
    	}
    ) 
    
    // 输出:
    // promise is pending:  Promise {<pending>}
    // promise is resolved:  Promise {<fulfilled>: 1}
    // value of promise when resolved:  1
    // nextPromise:  Promise {<fulfilled>: 2}
    
  • 向期约实例添加拒绝处理程序。

    const promise = new Promise(
    	(resolve, reject) => {
    		setTimeout(
    			() => {
    				reject(3)
    				console.log('promise is rejected: ', promise)
    			},
    			1000
    		)
    	}
    )
    
    console.log('promise is pending: ', promise)
    
    const nextPromise = promise.then(
    	null,  // 不提供解决处理程序
    	(reason) => {
    		console.log('reason of promise when rejected: ', reason )  // 打印期约 promise 落定为拒绝期约后的理由
    		return 4
    	}  // 拒绝处理程序
    ) 
    
    promise.then(
    	null,
    	() => {
    		console.log('nextPromise: ', nextPromise)
    	}
    ) 
    
    // 输出:
    // promise is pending:  Promise {<pending>}
    // promise is rejected:  Promise {<rejected>: 3}
    // reason of promise when rejected:  3
    // nextPromise:  Promise {<fulfilled>: 4}
    
  • 向期约实例添加解决处理程序、拒绝处理程序。

    function start(randomState) {
    	const promise = new Promise(
    		(resolve, reject) => {
    			setTimeout(
    				() => {
    					// 随机落定
    					switch(randomState) {
    						case 0:
    							resolve('resolved')
    							console.log('promise is resolved: ', promise)
    							break
    						case 1:
    							reject('rejected')
    							console.log('promise is rejected: ', promise)
    							break
    						default:
    							break
    					}
    				},
    				1000
    			)
    		}
    	)
    	
    	const nextPromise = promise.then(
    		(value) => {
    			console.log('value of promise when resolved: ', value )
    			return 'resolved value'
    		},  // 解决处理程序
    		(reason) => {
    			console.log('reason of promise when rejected: ', reason )
    			return 'rejected reason'
    		}  // 拒绝处理程序
    	) 
    
    	promise.then(
    		() => {
    			console.log('nextPromise: ', nextPromise)
    		},
    		() => {
    			console.log('nextPromise: ', nextPromise)
    		}
    	) 
    }
    
    const randomState = Math.round(Math.random())
    start(randomState)
    
    // 可能的输出_01:
    // promise is resolved:  Promise {<fulfilled>: 'resolved'}
    // value of promise when resolved:  resolved
    // nextPromise:  Promise {<fulfilled>: 'resolved value'}
    
    // 可能的输出_02:
    // promise is rejected:  Promise {<rejected>: 'rejected'}
    // reason of promise when rejected:  rejected
    // nextPromise:  Promise {<fulfilled>: 'rejected reason'}
    
  • 在处理程序中抛出错误。

    const promise = Promise.resolve()
    
    const nextPromise = promise.then(
    	() => {
    		throw 'error'  // 抛出错误
    	}
    ) 
    
    // 为期约 nextPromise_03 添加拒绝处理程序,捕获并处理异步错误
    nextPromise.then(
    	null,
    	() => {
    		console.log('nextPromise: ', nextPromise)
    		console.log('error in nextPromise is resolved')
    	}
    ) 
    
    // 输出:
    // nextPromise:  Promise {<rejected>: 'error'}
    // error in nextPromise is resolved
    
  • 处理程序返回待定期约。

    const promise = Promise.resolve()
    
    const nextPromise = promise.then(
    	() => {
    		return new Promise(
    			(resolve, reject) => {
    				setTimeout(resolve, 1000)
    			}
    		)  // 返回一个 1s 后落定的待定期约
    	}
    )
    
    setTimeout(console.log, 0, 'nextPromise:', nextPromise)
    setTimeout(console.log, 2000, 'nextPromise after 2s:', nextPromise)
    
    // 输出:
    // nextPromise: Promise {<pending>}
    // nextPromise after 2s: Promise {<fulfilled>: undefined}
    

Promise.prototype.catch()

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

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

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

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

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

    提供一个参数:reason,当前期约实例落定为拒绝期约后的理由。

  • 返回值:
    期约,期约的状态会动态改变:

    • 初始状态

      在排期的任务执行完毕之前,期约处于待定状态。

    • 落定状态

      在排期的任务执行完毕之后,期约落定。

      可能的落定状态:

      • 继承当前期约实例的状态

      情形:

      • 没有提供处理程序

      期约会继承当前期约实例的状态,并且继承当前期约实例的值。

      • 解决

      情形:

      • 提供处理程序。

      解决期约的值为处理程序的返回值。

      如果处理程序没有返回值,则解决期约的值为 undefined 。

      • 拒绝

        情形:

        • 在处理程序中抛出错误。
        • 处理程序返回一个拒绝期约(即抛出异步错误)。

        拒绝期约的理由为:

        • 处理程序抛出的错误信息。
        • 处理程序返回的拒绝期约的理由。
    • 保持待定状态

      情形:

      • 处理程序返回一个待定期约。

      切换到落定状态:

      • 直到处理程序返回的待定期约落定后,期约才会根据返回期约的落定状态进行落定。

事实上期约的原型方法 catch() 是一个语法糖,相当于调用期约的原型方法 then(null, onRejected) 。

示例:

  • 使用期约的原型方法 catch() ,向期约实例添加拒绝处理程序。
    const promise = Promise.reject()
    const onRejected = function() {
    	console.log('rejected is resolved')
    }
    
    const nextPromise = promise.catch(onRejected)  // 添加拒绝处理程序
    
    // 相当于
    const nextPromise_01 = promise.then(null, onRejected)
    
    promise.catch(() => { console.log('nextPromise:', nextPromise) })
    
    // 输出:
    // rejected is resolved
    // rejected is resolved
    // nextPromise: Promise {<fulfilled>: undefined}
    

Promise.prototype.finally()

期约的原型方法 finally() 用于为期约实例添加异步执行的最终处理程序。

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

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

  • 功能:
    为期约实例添加异步执行的最终处理程序。

  • 接收一个参数(可选):
    函数,onFinally 处理程序(姑且称为最终处理程序), 在当前期约实例落定后被调用,无论落定状态是解决还是拒绝。

  • 返回值:
    期约,期约的状态会动态改变:

    • 初始状态

      在排期的任务执行完毕之前,期约处于待定状态。

    • 落定状态

      在排期的任务执行完毕之后,期约落定。

      可能的落定状态:

      • 继承当前期约实例的状态

      情形:

      • 没有提供处理程序

      期约会继承当前期约实例的状态,并且继承当前期约实例的值。

      • 解决

      情形:

      • 提供处理程序。

      解决期约的值为处理程序的返回值。

      如果处理程序没有返回值,则解决期约的值为 undefined 。

      • 拒绝

        情形:

        • 在处理程序中抛出错误。
        • 处理程序返回一个拒绝期约(即抛出异步错误)。

        拒绝期约的理由为:

        • 处理程序抛出的错误信息。
        • 处理程序返回的拒绝期约的理由。
    • 保持待定状态

      情形:

      • 处理程序返回一个待定期约。

      切换到落定状态:

      • 直到处理程序返回的待定期约落定后,期约才会根据返回期约的落定状态进行落定。

示例:

  • 使用期约的原型方法 finally() ,向期约实例添加最终处理程序。
    // 创建一个 1s 后落定为解决期约的期约实例
    const promise = new Promise(
    	(resolve, reject) => {
    		setTimeout(resolve, 1000, 'promise')
    	}
    )
    const onFinally = function() {
    	console.log('run onFinally()')
    	return new Promise(() => {})
    }
    
    const nextPromise = promise.finally(onFinally)  // 添加最终处理程序
    
    setTimeout(console.log, 1000, 'nextPromise:', nextPromise)
    
    // 输出:
    // run onFinally()
    // nextPromise: Promise {<pending>}
    

关于拒绝期约

如前所述,拒绝期约会抛出一个异步错误。

异步错误的值为拒绝期约的理由。

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

期约落定为拒绝期约表示 “以某个理由拒绝期约”。

期约落定为拒绝期约的情形:

  • 在期约的执行函数中:

    • 调用执行函数提供的函数 reject()。

      期约的构造函数会返回一个拒绝期约,拒绝的理由为函数 reject() 的参数。

    • 抛出错误。

      期约的构造函数会返回一个拒绝期约,拒绝的理由为抛出的错误。

  • 在期约的处理程序中:

    • 返回一个拒绝期约。

      相应的期约方法会返回一个拒绝期约,拒绝的理由为处理程序返回的拒绝期约的理由。

    • 抛出错误。

      相应的期约方法会返回一个拒绝期约,拒绝的理由为抛出的错误。

可以使用任何值作为错误信息抛出错误,但建议统一使用错误类型创建的错误对象。

因为浏览器可以捕获错误信息中的栈追踪信息,这些信息对调试是非常关键的。

示例:

  • 在期约的执行函数中抛出错误。
    const promise = new Promise(
    	() => {
    		throw new Error('error')  // 抛出错误
    	}
    )
    console.log('promise:', promise)
    
    // 捕获并处理错误
    promise.catch(
    	(reason) => { console.log('resolved the error:', reason) }
    )
    
    // 输出:
    // Promise {<rejected>: Error: error
    // --at <anonymous>:3:10
    // --at new Promise (<anonymous>)
    // --at <anonymous>:1:17
    // resolved the error: Error: error
    // --at <anonymous>:3:10
    // --at new Promise (<anonymous>)
    // --at <anonymous>:1:17
    

期约连锁

期约连锁,即期约调用原型方法生成一个新期约,而后这个新期约也调用原型方法生成一个新期约,如此循环,不断地生成新期约,将期约串联起来。

期约连锁中提供给期约的处理程序会按照对应期约落定的顺序执行。

期约连锁的应用:

  • 异步顺序执行非异步代码。

    简单地在处理程序中添加需要执行的非异步代码。

  • 异步顺序执行异步代码。

    在一个期约中添加需要执行的异步代码,并在异步代码中落定期约,然后在处理程序中返回这个期约。

    如此,在返回期约落定之前,期约连锁的后续期约的处理程序不会被执行。

示例:

  • 使用期约连锁异步顺序执行非异步代码。

    const p = Promise.resolve()
    p.then(
    	() => {
    		console.log('run then() 01')
    	}
    ).finally(
    	() => {
    		console.log('run then() 02')
    	}
    ).then(
    	() => {
    		console.log('run then() 03')
    	}
    )
    
    // 输出:
    // run then() 01
    // run then() 02
    // run then() 03
    
  • 使用期约连锁异步顺序执行异步代码。

    const p = Promise.resolve()
    p.then(
    	() => {
    		return new Promise(
    			(resolve) => {
    				// 耗时 4s 的异步代码
    				setTimeout(
    					() => {
    						console.log('execute async code sequentially 01 consumed 4s ')
    						resolve()  // 落定期约
    					},
    					4000
    				)
    			}
    		)
    	}
    )
    .then(
    	() => {
    		return new Promise(
    			(resolve) => {
    				// 耗时 2s 的异步代码
    				setTimeout(
    					() => {
    						console.log('execute async code sequentially 02 consumed 2s ')
    						resolve()  // 落定期约
    					},
    					2000
    				)
    			}
    		)
    	}
    )
    .then(
    	() => {
    		return new Promise(
    			(resolve) => {
    				// 耗时 1s 的异步代码
    				setTimeout(
    					() => {
    						console.log('execute async code sequentially 03 consumed 1s ')
    						resolve()  // 落定期约
    					},
    					1000
    				)
    			}
    		)
    	}
    )
    
    // 输出:
    // execute async code sequentially 01 consumed 4s
    // execute async code sequentially 02 consumed 2s
    // execute async code sequentially 03 consumed 1s
    

期约合成

类型 Promise 提供两个静态方法,用于根据多个期约合成一个期约:

  • Promise.all()
  • Promise.race()

Promise.all()

期约的静态方法 Promise.all() :

  • 功能:
    根据多个期约合成一个期约,返回的期约只有在所有期约落定为解决期约时才会落定为解决期约。

  • 接收一个参数:
    可迭代对象,可迭代对象中的元素:

    • 可迭代对象的元素不是期约

      可迭代对象的元素会通过 Promise.resolve() 转换为解决期约。

    • 可迭代对象中没有元素

      方法 Promise.reolve() 会包装一个 undefined 值生成一个解决期约。

  • 返回值:
    期约,被称为合成期约,期约的状态会动态改变:

    • 待定

      情形:

      • 可迭代对象的期约中,至少有一个期约处于待定状态。
    • 解决

      情形:

      • 可迭代对象的期约全部落定为解决期约。

      合成解决期约的值为一个数组,数组的元素为可迭代对象的所有解决期约的值。

    • 拒绝

      情形:

      • 可迭代对象的期约中,至少有一个期约落定为拒绝期约。

      合成拒绝期约的理由为可迭代对象中的第一个拒绝期约的理由。

      处理合成拒绝期约时,会静默处理可迭代对象中的其它拒绝期约。

示例:

  • 可迭代对象的元素不是期约。

    const iterable = ['01', '02', '03']  // 可迭代对象的元素不是期约
    
    const compositePromise = Promise.all(iterable)
    
    setTimeout(
    	() => {
    		console.log('compositePromise', compositePromise)
    		compositePromise.then(
    			(values) => {
    				// 合成解决期约的值为一个数组
    				values.forEach(
    					(value, index) => {
    						console.log(`value ${index}:`, value)
    					}
    				)
    			}
    		)
    	},
    	1000
    )
    
    // 输出
    // compositePromise Promise {<fulfilled>: Array(3)}
    // value 0: 01
    // value 1: 02
    // value 2: 03
    
  • 可迭代对象中包含待定期约。

    const promises = [
    	Promise.resolve(),
    	new Promise(() => {})  // 待定期约
    ]
    
    const compositePromise = Promise.all(promises)
    
    setTimeout(console.log, 1000, 'compositePromise:', compositePromise)
    
    // 输出
    // compositePromise: Promise {<pending>}
    
  • 可迭代对象中包含拒绝期约。

    const promises = [
    	Promise.resolve(),
    	Promise.reject('reason_01'),  // 拒绝期约
    	Promise.reject('reason_02')
    ]
    
    const compositePromise = Promise.all(promises)
    
    compositePromise.catch(
    	() => {
    		console.log('compositePromise:', compositePromise)
    		console.log('rejected is resolved')
    	}
    )
    
    // 输出
    // compositePromise: Promise {<rejected>: 'reason_01'}
    // rejected is resolved
    

Promise.race()

期约的静态方法 Promise.race() :

  • 功能:
    根据多个期约合成一个期约,返回的期约是多个期约中第一个落定的期约的镜像。。

  • 接收一个参数:
    可迭代对象,可迭代对象中的元素:

    • 可迭代对象的元素不是期约

      可迭代对象的元素会通过 Promise.resolve() 转换为期约。

    • 可迭代对象中没有元素

      空可迭代对象会转换为一个调用 new Promise(() => {}) 创建的待定期约。

  • 返回值:
    期约,被称为包装期约,期约的状态会动态改变:

    • 待定

      情形:

      • 可迭代对象的期约全部处于待定状态。
    • 解决

      情形:

      • 可迭代对象的期约中,至少有一个期约落定为解决期约。

      包装解决期约的值为可迭代对象中第一个解决期约的值。

    • 拒绝

      情形:

      • 可迭代对象的期约中,至少有一个期约落定为拒绝期约。

      包装拒绝期约的理由为可迭代对象中第一个拒绝期约的理由。

      处理包装拒绝期约时,会静默处理可迭代对象中的其它拒绝期约。

可迭代对象的迭代顺序决定了期约的落定顺序。

示例:

  • 可迭代对象中第一个落定的期约。
    const promises = [
    	new Promise(() => {}),
    	new Promise(
    		(resolve) => {
    			setTimeout(
    				() => {
    					resolve('01')
    					console.log('resolved 01 consumed 2s ')
    				},
    				2000
    			)
    		}
    	),  // 2s 后落定的期约
    	new Promise(
    		(resolve) => {
    			setTimeout(
    				() => {
    					resolve('02')
    					console.log('resolved 02 consumed 1s ')
    				},
    				1000
    			)
    		}
    	)  // 1s 后落定的期约
    ]
    
    const packPromise = Promise.race(promises)
    
    packPromise.then(
    	() => {
    		console.log('packPromise:', packPromise)
    	}
    )
    
    // 输出
    // resolved 02 consumed 1s 
    // packPromise: Promise {<fulfilled>: '02'}
    // resolved 01 consumed 2s 
    

期约合成值

利用期约的特性:期约落定时产生一个值,期约的值会被期约的处理程序作为参数接收。在期约落定时提供期约的值,并使用期约连锁逐级接收并加工上一级期约的值,最终得到一个值,姑且称为期约合成值

示例:

  • 使用期约连锁生成一个期约合成值。

    // 处理程序
    function handler_01(str) {
    	return str + ' -> handler_01'  // 加工期约值
    }
    function handler_02(str) {
    	return str + ' -> handler_02'
    }
    function handler_03(str) {
    	return str + ' -> handler_03'
    }
    
    function compositeValue(str) {
    	return Promise.resolve(str)
    		.then(handler_01)
    		.then(handler_02)
    		.then(handler_03)  // 期约连锁
    }
    
    compositeValue('value').then(console.log)
    
    // 输出:
    // value -> handler_01 -> handler_02 -> handler_03
    
  • 提炼出一个通用函数,可以把任意多个函数作为处理程序,利用期约连锁加工一个值。

    function handler_01(str) {
    	return str + ' -> handler_01'
    }
    function handler_02(str) {
    	return str + ' -> handler_02'
    }
    function handler_03(str) {
    	return str + ' -> handler_03'
    }
    
    // 通用函数
    function compositeValue(...funcs) {
    	return (value) => funcs.reduce(  // 使用 Array 原型方法 reduce()
    		(promise, func) => promise.then(func),
    		Promise.resolve(value)
    	)
    }
    
    const composite = compositeValue(handler_01, handler_02, handler_03)
    
    composite('value').then(console.log)
    
    // 输出:
    // value -> handler_01 -> handler_02 -> handler_03
    
    // 补充说明:
    // Array 原型方法 reduce():
    // -- 第一个参数:归并函数,按顺序对每个数组元素执行该函数。
    // ---- 提供的参数:
    // ------ 第一个参数:上一次执行归并函数返回的值。
    // ------ 第二个参数:当前的数组元素。
    // -- 第二个参数(可选):任意值,初始值,即对第一个数字元素执行归并函数时作为归并函数的第一个参数。
    // -- 返回值:
    // ---- 任意值,最后执行归并函数返回的值。
    

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值