Promise
Promise 是 JavaScript 中用于处理异步操作的对象,它代表一个未来才会完成(或失败)的操作的结果。可以把它理解为“承诺”:现在无法获得结果,但将来一定会告知成功还是失败。
1-介绍
理解
1.抽象表达:
- Promise是一门新的技术(ES6规范)
- Promise 是 JS 中进行异步编程的新解决方案
备注:旧方案是单纯使用回调函数
2.具体表达:
- 从语法上来说:Promise是一个构造函数
- 从功能上来说:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
优势
指定回调函数的方式更加灵活
- 旧的:必须在启动异步任务前指定
- promise:启动异步任务=>返回
promie对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
支持链式调用,解决回调地狱的问题
2-基本使用
Promise的状态PromiseState
一个 Promise 必然处于以下几种状态之一:
- 待定(
pending): 初始状态,既没有被兑现,也没有被拒绝。 - 成功(
fulfilled): 意味着操作成功完成。 - 失败(
rejected): 意味着操作失败。
一个 Promise 的状态只能由 pending 变为 fulfilled,或者变为 rejected,且只能改变一次。
链式调用:Promise 对象具有 then 和 catch 方法来处理下一步的操作。因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是 Promise,所以它们可以被链式调用。
链式调用例子:
1 | // 模拟异步请求函数(返回 Promise) |
Promise对象的值PromiseResult
保存着对象『成功/失败』的结果,通过resolve()和reject()两个函数来修改,保存的值在then()方法中的回调函数中取出
1 | const p = new Promise((resolve, reject) => { |
Promise的基本流程

应用
封装读取文件:
1 | const fs = require('fs') |
封装ajax请求:
1 | <!DOCTYPE html> |
3-详细使用
Promise API
Promise 构造函数
Promise 构造函数接收一个执行器 executor,来启动这个 Promise。
1 | new Promise(executor) |
参数 :
resolve 和 reject,分别表示操作成功和操作失败。
1 | new Promise((resolve, reject) => { |
executor函数:执行器(resolve, reject) => { }resolve函数:内部定义成功时调用的函数value => {}reject函数:内部定义失败时调用的函数reason => {}
返回值:
当通过 new 关键字调用 Promise 构造函数时,它会返回一个 Promise 对象。当 resolveFunc 或者 rejectFunc 被调用时,该 Promise 对象就会变为已解决(resolved)。
注意:executor会在Promise内部立即同步调用.异步操作在执行器中执行
new Promise(executor)这行代码执行时,**executor会立刻运行**(不是等到后面.then()被调用才运行)。executor是同步执行的,但它的内部可以启动异步操作(比如setTimeout、fetch、fs.readFile等)。- 异步操作的结果通过调用
resolve(value)或reject(reason)来通知 Promise 状态变更。
1 | console.log('1. 开始'); |
执行结果:
1 | 1. 开始 |
- 同步部分:
Promise构造函数的调用、executor的初始执行、.then()方法的调用。- 异步部分:
executor中启动的异步操作(如网络请求、定时器等);.then()/.catch()中回调函数的实际执行(总是异步,走微任务队列)。
Promise.prototype.then
Promise.prototype.then 方该方法返回的结果依然是一个 Promise 对象,意味着我们可以进行链式调用。
语法:
1 | p.then(onFulfilled, onRejected); |
参数:
onFulfilled函数:成功的回调函数(value) => {}onRejected函数:失败的回调函数(reason) => {}
返回值:
如果 then 中的回调函数:
- 返回了一个值,那么
then返回的 Promise 将会成为接受状态(fulfilled),并且将返回的值作为接受状态的回调函数的参数值。 - 没有返回任何值,那么
then返回的 Promise 将会成为接受状态(fulfilled),并且该接受状态的回调函数的参数值为 undefined。 - 抛出一个错误,那么
then返回的 Promise 将会成为拒绝状态(rejected),并且将抛出的错误作为拒绝状态的回调函数的参数值。 - 返回一个已经是接受状态(
fulfilled)的 Promise,那么then返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。 - 返回一个已经是拒绝状态(
rejected)的 Promise,那么then返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。 - 返回一个未定状态(
pending)的 Promise,那么then返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
Promise.prototype.catch()
catch() 方法返回一个 Promise,并且处理操作失败的情况。它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同。但是用起来更方便了,可以看作是一种 语法糖(让我们执行某些操作更加方便简洁的方法)。
语法:
1 | p.catch(onRejected); |
等同于:
1 | p.then(undefined, onFulfilled); |
参数:
onRejected:操作失败时的回调函数(reason) => {}
返回值:
- 返回一个
Promise对象。如果onRejected抛出一个错误或返回一个本身失败的Promise,那么catch()返回的Promise的状态为rejected;否则,状态为fulfilled。
Promise.resolve()
Promise.resolve() 是一个函数对象方法,意味着只能用在函数对象 Promise 上。它接收一个 value 参数,返回一个这个给定值解析后的 Promise 对象。
语法:
1 | Promise.resolve(value); |
参数:
value:被解析的参数,可以是一个值,也可以是一个 Promise 对象,或者是一个 thenable
返回结果:
- 若给定值为一个普通数、字符串或数组等等之类的,则返回一个成功的 Promise
- 若参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。
- 由此可知,
resolve()方法可以返回一个成功或者失败的 Promise 对象
Promise.reject()
Promise.reject() 方法返回一个失败的 Promise 对象,接收一个 value 参数,表示失败的原因。
语法:
1 | Promise.reject(reason); |
参数:
reason:表示操作失败的原因
返回值:
- 返回一个失败(状态为
rejected)的 Promise 对象
Promise.all()
Promise.all() 方法接收由多个 promise 组成的可迭代类型(Array、Map、Set等),然后返回一个新的 promise。它等待所有 promise 完成,或者第一个失败的 promise。
1 | Promise.all(iterable); |
参数:
iterable:可迭代类型,一般为数组
返回值:
- 若传入的参数为空的可迭代对象,例如
Promise.all([]),则返回一个已完成状态(fulfilled)的 Promise - 若传入的参数不包含任何 promise,则返回一个异步完成(
asynchronously resolved)的 Promise。例如Promise.all([1, 2]) - 其它情况下返回一个处理中(
pending)的 Promise。只有接收的参数的 promise 全部成功,这个返回的 Promise 才会是成功的,只要有一个失败,则返回一个失败的 Promise,且返回的值为第一个失败的值。 - 若所有 promise 都成功,则返回的 Promise 的值为一个包含了多个结果的数组
Promise.all 的同步与异步:
- 当如果传入的可迭代对象是空的,就是同步;其他情况都是异步。
举例:
参数为非空的数组,异步的:
1 | const p = Promise.all([1]) |
注意:此时(1)输出状态为 pending,因为同步代码先执行,而此时异步 promise 的结果还未知
参数为空数组,同步的:
1 | const p1 = Promise.all([]) |
注意:此时(1)输出状态为 fulfilled,说明此时 Promise.all 是同步的。
Promise.all 方法举例
1 | // 参数为空的数组 |
Promise.race()
Promise.race() 也是接收一个可迭代类型,例如一个包含了多个 promise 的数组。然后返回一个新的 promise,第一个完成的 promise 的结果状态就是最终返回的结果状态。
需要注意,这里最终返回的结果的状态不一定是参数数组中位于前面的 promise,具体要看哪一个最先完成,所以 race() 方法的返回结果可能为成功或失败的。
语法:
1 | Promise.race(iterable); |
参数:
iterable:可迭代对象,一般为包含多个 promise 的数组
返回值:
- 返回一个 promise,只要给定的可迭代对象中的 promise 有一个先完成(成功或失败),就采用它的值作为最终 promise 的值,状态与这个完成的 promise 相同。
Promise.race 的异步性(没有同步性):
1 | const p = Promise.race([Promise.resolve(10), Promise.resolve(20)]) |
注意:Promise.race 没有同步性
Promise的几个关键问题
1-如何修改对象的状态
resolve(value):如果当前是 pending 就会变为 resolvedreject(reason):如果当前是 pending 就会变为 rejected- 抛出异常:如果当前是 pending 就会变为 rejected
2-一个promise指定多个成功/失败回调函数,都会调用吗?
当 promise 改变为对应状态时都会调用。
1 | let p = new Promise((resolve, reject) => { |
3-改变promise状态和指定回调函数谁先谁后?
- 都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
- 如何先改状态再指定回调?
- 在执行器中直接调用
resolve()/reject() - 延迟更长时间才调用
then()
- 在执行器中直接调用
- 什么时候才能得到数据?
- 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
- 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
1 | // 正常情况,先指定回调再改变状态 |
4-promise.then() 返回的新promise 的结果状态由什么决定?
- 简单表达:由
then()指定的回调函数执行的结果决定 - 详细表达:
- 如果抛出异常,新 promise 变为
rejected, reason为抛出的异常 - 如果返回的是非 promise 的任意值,新promise变为
resolved,value 为返回的值 - 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
- 如果抛出异常,新 promise 变为
5-promise 如何串连多个操作任务?
- promise的
then()返回一个新的 promise,可以看成then()的链式调用 - 通过
then的 链式调用串连多个同步/异步任务
.then()中可以处理同步任务,也可以处理异步任务。处理异步任务时需要新创建一个Promise。
1 | // 任务1 |
6-promise异常传透?
在 Promise 的链式调用链中,当所有的 .then 中都没有指定错误处理的回调,则前面出现的异常会在最后失败的回调中处理。
1 | new Promise((resolve, reject) => { |
注意:
.catch所谓的异常穿透并不是一次失败状态就触发catch,而是一层一层的传递下来的- 异常穿透的前提条件是所有的
.then都没有指定失败状态的回调函数。 - 如果
.catch前的所有.then都指定了失败状态的回调函数,.catch就失去了意义。
7-值传透
值穿透指的是,链式调用的参数不是函数时,会发生值穿透,就传入的非函数值忽略,传入的是之前的函数参数。
1 | Promise.resolve(1) |
只有传入的是函数才会传递给下一个链式调用。
1 | Promise.resolve(1) |
8-中断Promise链
- 什么是中断:当使用 promise 的
then链式调用时,在中间中断,不再调用后面的回调函数 - 中断的方法:在回调函数中返回一个
pendding状态的 promise 对象
如何返回一个 pendding 状态的 promise 对象:
1 | return new Promise(() => { }) // pendingCopy to clipboardErrorCopied |
或者使用 Promise.race([]),race 方法接收一个空的可迭代对象时,该 Promise 会一直处于 pendding 状态。
1 | return Promise.race([]) // pendingCopy to clipboardErrorCopied |
中断之前:
1 | Promise.resolve(1) |
中断之后:
1 | Promise.resolve(1) |
4-手写Promise
这个先空着面试的时候再看
5- async await
async 函数
1 | async function main() { |
await表达式
await右侧的表达式一般为promise对象,但也可以是其它的值- 如果表达式是
promise对象,await返回的是promise成功的值 - 如果表达式是其它值,直接将此值作为
await的返回值 async/await中真正起作用的是await,可以把async关键字简单的当作一个 标识符. 因为如果 异步函数 中不使用await关键字,其执行基本上跟普通函数没有什么区别,但是对于 异步函数 的返回值还是会和 普通函数 有区别
注意
await必须写在async函数中,但async函数中可以没有await- 如果
await的promise失败了,就会抛出异常,需要通过try..catch捕获处理 - 当一个 promise 抛出异常时,要使用
try...catch捕获异常,必须使用await使回调函数变为异步回调,否则try...catch无法捕获到错误。
1 | // 正确示例 |
- 在
async function中是否有await是不一样的。若async function中没有await,则这个函数就是纯同步执行,只是返回值是一个Promise。 - 在
async function中添加了await后,此行代码之前为立即执行,此行代码之后的语句一起作为 整体 变为微队列中的异步回调。相当于给后面的代码套了一层then方法。 - 在使用
try..catch捕获了await中的异常后,await后面代码将不会再执行。
1 | // 不使用 await |
