接前面的:
- JavaScript引擎与运行时入门小记:在哪些环境下可以运行JavaScript
- JavaScript学习笔记(一):理解概念,一等函数、编程范式、原型、单线程
- JavaScript学习笔记(二):全局对象globalThis,this用法,全局对象们
- JavaScript学习笔记(三):7种原始数据类型,对象类型(引用类型),typeof,原始类型包装器
- JavaScript学习笔记(四):ES6模块ESM,ES6之前CJS,Nodejs如何区分传统脚本与ESM模块文件
- JavaScript学习笔记(五):Event loop 简简单单一个东西,术语太乱了
- JavaScript学习笔记(六):Promise 基本概念,一些简单例子
继续学习JavaScript。
Promise
Promise是现代JavaScript中异步编程的基础。
Promise应该翻译成 承诺?承诺会提供一个值,如果不能提供值会提供错误原因?
概念
Promise是由异步函数返回的一个对象,它表示操作的当前状态。在将Promise返回给调用者的时候,操作通常尚未完成,但是Promise对象提供了方法来响应其最终成功或失败的场景。
Promise 有三种状态:
- pending:待定状态,初始状态。
- fulfilled:已实现状态,表示操作已成功完成。
- rejected:已拒绝状态,表示操作失败。
处于pending状态的Promise最终状态可以是以某个值 进入fulfilled状态或以某个原因(error)进入 rejected状态。当其中任何一种情况发生时,由Promise的then
方法排队的相关处理程序将被调用。如果在附加相应处理程序时Promise已经完成或拒绝,处理程序将被立即调用。
fulfilled或rejected状态都可称为settled(已完成)状态。
构造
Promise只能使用new来构造:
1 |
|
Promise构造函数接受一个执行器函数,这个函数长这样:
1 2 3 4 5 |
|
注:执行器函数是在promise创建时,同步执行的!执行器函数的返回值没有用。
它有两个参数 resolveFunc
和 rejectFunc
,分别用于将 Promise 状态从 Pending 改为 Fulfilled 或 Rejected。
resolveFunc(value)
:将 Promise 的状态从 Pending 转变为 Fulfilled(已实现),并携带一个指定的解决值value
。这表示异步操作成功完成。rejectFunc(reason)
:将 Promise 的状态从 Pending 转变为 Rejected(已拒绝),并携带一个指定的拒绝原因reason
。这表示异步操作遇到了错误或失败。
由于这两个函数是Promise构造时自动生成的,要想在执行器函数的外边执行它,很不方便,需要通过变量先传递出去:
1 2 3 4 5 |
|
为了减轻这个工作,ES2023为Promise引入了静态函数withResolvers
,它可以同时返回创建的promise对象以及对应的resolveFunc
和rejectFunc
:
1 |
|
不过Node.js尚不支持它。
作为例子,对于Promise创建,可以写一个返回promise的函数,让执行器函数使用setTimeout模拟异步操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
链式操作
Promise通过then()、catch()、finally() 实现链式操作,它们三个都会返回一个新的promise对象:
- promise.prototype.then()
- promise.prototype.catch()
- promise.prototype.finally()
其参数:
1 2 3 4 5 6 |
|
其中,涉及的三个回调函数,长这个样子:
1 2 3 |
|
在then()
和catch()
中:如果onFulfilled
不是函数的话,内部它会被替换成(x)=>x
;如果onRejected
不是函数的话,内部会被替换成(x) => { throw x; }
。
注:
catch(onRejected)
是then(null, onRejected)
的简写形式;而finally(onFinally)
和then(onFinally, onFinally)
行为有些像,不管怎样回调都会执行。但不用于then,finally中的回调函数没有参数。
所以,先看then()
好了,看看它返回的新的 Promise 是怎么来的?
不管执行then()
中哪一个回调函数:
回调函数 | 新的 promise | 备注 |
---|---|---|
无返回值 | fulfilled状态,携带值是undefined? | - |
返回一个值 | fulfilled状态,使用这个返回值 | - |
返回promise对象 | 与该promise的状态和值保持一致 | - |
抛出异常 | rejected状态,携带抛出的异常 | - |
在回调函数中,我们可以抛出异常,没有返回值,返回一个值,或者返回另一个promise对象。不管怎样,then()、catch() 都会基于其结果返回一个新的promise对象。不用我们传入执行器函数来手动创建promise对象。
对于finally(onFinally)
返回的promise来说,onFinally的返回值被默认被忽略(除非其返回值是一个rejected状态的promise)。
接前面构造的例子,继续通过例子看看then()
的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
运行结果:
1 2 3 |
|
运行结果:
1 2 |
|
其他成员
Promise还有其他一些成员,都会返回一个新的promise对象:
- Promise.all()
- Promise.any()
- Promise.race()
- Promise.reject()
- Promise.resolve()
前三个都接收一个可迭代的对象(通常是包含多个 Promise 的数组),返回一个新的 Promise。后两个可以看作是用于创建指定状态的Promise,具体看后面例子。
reject 始终会创建新的promise,resolve不一定,而且resolve还可以接受thenable。
例子
抛开文字,通过简单的例子找找感觉:
then() 返回的promise
回调有返回值,p为fulfilled状态,且使用该返回值:
1 2 3 4 5 6 7 8 9 |
|
回调无返回值,p为fulfilled状态,且使用undefined:
1 2 3 4 5 6 7 8 9 |
|
回调返回promise,p状态与值与回调返回的promise一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
回调抛出异常,p状态为rejected:
1 2 3 4 5 6 7 8 9 10 11 |
|
刚才用了then的onFulfulled,使用onRejected时,结论与前面一样:
1 2 3 4 5 6 7 8 9 10 11 |
|
catch() 返回的promise
与then其实一样
1 2 3 4 5 6 7 8 9 10 11 |
|
finally() 返回的promise
不受回调返回值影响,与原promise的状态和值都一样
1 2 3 4 5 6 7 8 9 10 11 |
|
回调返回fulfilled的promise,对p没影响
1 2 3 4 5 6 7 8 9 10 11 |
|
回调返回rejected的promise,对p有影响!!
1 2 3 4 5 6 7 8 9 10 11 |
|
回调抛出异常,和then预期一样
1 2 3 4 5 6 7 8 9 10 11 |
|
Promise.reject()生成的promise
Promise.reject()等价于
1 |
|
所以:
1 2 3 4 5 6 7 8 9 10 |
|
Reomise.resolve()返回的promise
这个静态函数用于将给定值变成一个promise!
1 2 3 4 5 6 7 |
|
如果给他一个promise,则会直接返回
1 2 3 4 5 6 7 8 9 10 |
|
额,应该这样
1 2 3 4 |
|
resolve()还接收thenable。thenable需要实现接收两个回调函数的then()
方法,早期Promise标准化之前有各种promise实现都实现了这个theable协议。
1 2 3 4 5 6 7 8 9 10 11 |
|
这个神奇的函数,还能接收和Promise的构造函数长的一样的非promise构造函数
1 2 3 4 5 6 7 8 9 10 |
|
参考
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise