为什么使用Promise:
在传统的异步编程中,如果异步之间存在依赖关系,只能使用层层嵌套的回调函数来满足这种依赖,如果嵌套的层数过多,降低可读性和可维护性,产生所谓的“回调地狱”。
Promise提供了全新的异步编程的一种解决方案。将回调嵌套改为链式调用,将异步的操作以同步操作的方式表达出来。
简单释义:
Promise是一个对象,可以理解为一个状态机,保存着某个未来才会结束的事件的结果。从而可以获取异步操作的消息。它提供了统一的API。
Promise A+规范:
两个核心规则:
- Promise本质是一个状态机,有3种状态:Pending(等待态)、Fulfilled(执行态)、Rejected(拒绝态),而且状态的变更是单向的,只能从Pending -> Fulfilled 或 Pending -> Rejected,状态变更不可逆;
- then()方法接收两个可选参数,分别对应状态改变时触发的回调。then方法返回一个promise。then 方法可以被同一个 promise 调用多次。
缺点
- 一旦创建实例,无法取消,会立即执行;
- 在pending状态改变之前,无法知道进度(是才开始,还是即将结束)
- 对比rx,不能吐出多个值。
执行机制
new的实例会立即执行,resolve和reject异步回调函数会在本次事件循环结束后才会执行。
基本用法:
Promise对象是一个构造函数,故使用new操作符生成一个Promise实例。
最简单的Promise的使用:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result')
},
1000);
})
p1.then(res => console.log(res), err => console.log(err))
语法:
new Promise(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void): Promise<any>
Promise<any>.then<any, never>(onfulfilled?: (value: any) => any, onrejected?: (reason: any) => PromiseLike<never>): Promise<any>
分析Promise的调用流程:
- 构造函数接受衣蛾executor(),在new Promise()时会立即执行这个回调函数;
- executor()内部的异步任务被放入宏/微任务队列,等待执行;
- 调用then()时,会收集resolve/reject回调函数,放入成功/失败队列;
- 当executor()的异步任务被执行,触发resolve/reject,从成功/失败队列中取出回调依次执行。
观察者模式
收集依赖 -> 触发通知 -> 取出依赖执行 的方式,被广泛运用于观察者模式的实现,在Promise里,执行顺序是 then收集依赖 -> 异步触发resolve -> resolve执行依赖。
常用API
API | 作用 |
---|---|
Promise.prototype.then() | 第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数,返回一个新的Promise实例 |
Promise.prototype.catch() | 指定发生错误时的回调函数,错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获 |
Promise.prototype.finally() | 不管 Promise 对象最后状态如何,都会执行的操作,这是ES2018引入标准的 |
Promise.all() | 将多个 Promise 实例,包装成一个新的 Promise 实例;(所有成功才成功,一个失败就失败) |
Promise.race() | 同样是将多个 Promise 实例,包装成一个新的 Promise 实例(最后的状态取决于最先改变状态的实例) |
Promise.allSettled() | 有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束,,而且状态总是fulfilled(这是ES2020引入) |
Promise.any() | 接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例(所有失败才失败,衣蛾成功就成功) |
Promise.resolve() | 将现有对象转为 Promise 对象 |
Promise.reject() | 返回一个新的 Promise 实例,该实例的状态为rejected |
异常处理
分析问题:
- 如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,不会退出进程、终止脚本执行,即不会有任何反应;
- Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
处理方式
- 一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误;
- then()和catch()要成对出现,then在前,catch在后.。