跳转到内容

.catch 与 .finally

刀刀
0字
0分钟
2026/8/16

目前Promise A+的标准只包含了 .then,并没有包含其余的方法,这些方法都是 ES6 标准中新增的,因此 A+ 规范的文档没有这些事件的描述。推荐去 MDN 查看。

回顾

先来回顾一下之前做了啥,看一下完整的代码:

js
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function runMicrotasks(fn) {
  if (typeof queueMicrotask === 'function') {
    queueMicrotask(fn)
  } else if (
    typeof process === 'object' &&
    typeof process.nextTick === 'function'
  ) {
    process.nextTick(fn)
  } else if (typeof MutationObserver === 'function') {
    const observer = new MutationObserver(fn)
    const textNode = document.createTextNode(String(Math.random()))
    observer.observe(textNode, { characterData: true })
    // 当节点的内容发生变化,就会异步执行前面的fn函数
    textNode.data = String(Math.random())
  } else {
    setTimeout(fn, 0)
  }
}

function isPromiseLike(obj) {
  return typeof obj?.then === 'function'
}

class MyPromise {
  #state = PENDING // 修改为内部私有
  #value
  #handlers = [] // 保存onFulfilled和onRejected函数
  constructor(executor) {
    const resolve = (val) => {
      this.#setState(FULFILLED, val)
    }

    const reject = (reason) => {
      this.#setState(REJECTED, reason)
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  // 修改状态和值
  #setState(state, value) {
    if (this.#state !== PENDING) return
    this.#value = value
    this.#state = state
    this.#runTask()
  }

  #runTask() {
    runMicrotasks(() => {
      if (this.#state !== PENDING) {
        this.#handlers().forEach((cb) => cb())
        this.#handlers = []
      }
    })
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.#handlers.push(() => {
        try {
          const cb = this.#state === FULFILLED ? onFulfilled : onRejected
          const res = typeof cb === 'function' ? cb(this.#value) : this.#value
          if (isPromiseLike(res)) {
            resolve(res.then(resolve, reject))
          } else {
            resolve(res)
          }
        } catch (err) {
          reject(err)
        }
      })
      this.#runTask()
    })
  }
}

const p = new MyPromise((resolve, reject) => {
  resolve(1)
})

p.then((res) => {
  console.log('p1', res)
})

console.log('end')

MyPromise 接收一个回调函数,由于它是同步任务,因此会立即执行这个函数。执行完毕后,会调用 resolvereject 来改变状态。状态只会改变一次,发生改变后不再会改变。如果函数内部抛出了错误,最终状态也会变为 reject,因此用了 try...catch 来捕获错误。

需要注意的是,只能捕获同步的错误,异步的错误无法捕获。

.then 方法返回的是一个新的 MyPromise,这里不能用旧的是因为旧的 MyPromise 状态已经发生改变了,无法再改变。因此需要返回一个新的 MyPromise,并且根据上一个 MyPromise 的状态来决定下一个 MyPromise 的状态,即链式调用。

.then 的方法是等待状态改变后再执行,因此先用一个数组保存起来,并判断结果是不是一个 Promise,如果是则返回其 .then 后的结果,否则直接返回结果。最后在状态改变后,执行保存的函数。由于 .then 内是微任务,不能同步执行,这里的处理方式是判断当前的环境可用的异步方法,然后调用它。如果都不适用,则采用定时器。

.catch

.catch 方法是 .then 的语法糖,它只接收一个参数,即回调函数 onRejected,之前实现 .then 方法时接收两个参数,参数一函数 onFulfilled 是成功状态执行的;参数二函数 onRejected 是失败状态执行的。因此 .catch 方法可以理解为 .then(null, onRejected)

js
class MyPromise {
  // ...

  catch(onRejected) {
    return this.then(null, onRejected) 
  } 
}

.finally

.finally 方法也是 .then 的语法糖,它接收一个参数,即回调函数 onFinally,无论状态是成功还是失败,都会执行这个函数。但是在执行该回调函数时,是不会接收参数的,它只会把参数传递下去。

js
class MyPromise {
  // ...

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  finally(onFinally) {
    return this.then(
      (res) => {
        onFinally() 
        return res 
      }, 
      (err) => {
        onFinally() 
        throw err 
      }, 
    ) 
  } 
}

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as v_duyilin v_duyilin

页面历史

刀刀博客累计访客 人;文档累计访问量共