技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTJS

Promise 不会?看这里:史上最通俗易懂的 Promise 入门与实现

所属馆藏
JavaScript
文件格式
Markdown
原始路径
JavaScript/53-Promise不会??看这里!!!史上最通俗易懂的Promise!!!
本文目录10 个章节
  1. 一、什么是 Promise,解决什么问题
  2. 二、回调函数和回调地狱
  3. 三、原生 Promise 基础用法
  4. 四、Promise 的并发组合
  5. 五、原文插图
  6. 六、手写 Promise:先实现状态和回调队列
  7. 七、原文代码中的问题说明
  8. 八、Promise、async/await 和错误传播
  9. 九、总结
  10. 参考资料

Promise 不会?看这里:史上最通俗易懂的 Promise 入门与实现

Category(分类): JavaScript Status: 已整理(2026)

本文保留原文从回调函数、Promise 基础用法、then/catchall/race 到手写 Promise/A+ 解析过程的完整主线。原文中的状态、关键字、变量名拼写错误,缺少返回值和不完整构造器已修正;同时补充原生 Promise 与 Promise/A+、thenable、微任务、取消请求和现代静态方法的边界。

原文:Promise 不会??看这里!!!

一、什么是 Promise,解决什么问题

Promise 是表示异步操作最终结果的对象。它的状态通常有:

  • pending:等待中;
  • fulfilled:成功完成;
  • rejected:失败。

Promise 的状态只能从 pending 变化一次,变化后不能再次改变。注意“Promise 对象存在”不等于“异步操作已经开始”:new Promise(executor)立即同步执行 executor,异步性通常来自 executor 内部的定时器、网络请求、文件操作等:

console.log('before')

const promise = new Promise(resolve => {
  console.log('executor runs now')
  setTimeout(() => resolve('done'), 10)
})

promise.then(value => console.log(value))
console.log('after')

// before
// executor runs now
// after
// done

Promise 主要帮助我们:

  • 用链式数据流替代深层回调嵌套;
  • 统一表达成功、失败和异常传播;
  • 组合多个异步操作;
  • async/await 建立在可组合的 Promise 语义上。

Promise 本身不是“自动把同步代码变成异步”的魔法,也不会自动取消网络请求或解决所有并发竞态。

二、回调函数和回调地狱

简单回调可以完成异步通知:

function runIfValid(value, callback) {
  if (value > 10 && typeof callback === 'function') {
    callback(value)
  }
}

runIfValid(11, value => {
  console.log('callback:', value)
})

当多个操作必须按前后顺序嵌套时,代码可能变得难以维护:

loadUser((userError, user) => {
  if (userError) return handleError(userError)

  loadOrders(user.id, (orderError, orders) => {
    if (orderError) return handleError(orderError)

    loadProducts(orders, (productError, products) => {
      if (productError) return handleError(productError)
      render(user, products)
    })
  })
})

Promise 可以把顺序关系写成链:

loadUser()
  .then(user => loadOrders(user.id))
  .then(orders => loadProducts(orders))
  .then(products => render(products))
  .catch(handleError)

function render(products) {
  console.log(products)
}

function handleError(error) {
  console.error(error)
}

Promise 让控制流更平坦,但业务仍需要明确错误、取消、重试、超时和并发策略。

三、原生 Promise 基础用法

3.1 创建 Promise

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true

    if (success) {
      resolve('我是成功结果')
    } else {
      reject(new Error('操作失败'))
    }
  }, 100)
})

executor 接收 resolvereject 两个函数:

  • 调用 resolve(value) 表示成功;
  • 调用 reject(reason) 表示失败;
  • executor 抛出的异常会自动使 Promise rejected;
  • 第一次状态转换生效,后续调用无效。

推荐用 Error 对象表示异常原因,而不是直接 reject 字符串:

const failed = new Promise((resolve, reject) => {
  reject(new Error('network unavailable'))
})

failed.catch(error => {
  console.log(error.message) // network unavailable
})

3.2 then 链式调用

promise
  .then(value => {
    console.log('resolved:', value)
    return value.length
  })
  .then(length => {
    console.log('length:', length)
  })
  .catch(error => {
    console.error('rejected:', error)
  })

每一个 then 都返回新的 Promise。处理函数返回的普通值会传给下一个成功处理函数;返回 Promise 或 thenable 时,后续会等待它;抛错则进入后续拒绝路径:

Promise.resolve(1)
  .then(value => value + 1)
  .then(value => new Promise(resolve => {
    setTimeout(() => resolve(value * 2), 10)
  }))
  .then(value => console.log(value)) // 4

如果某一步不返回值,下一个成功处理函数得到 undefined

Promise.resolve('data')
  .then(value => {
    console.log(value)
    // 没有 return
  })
  .then(value => console.log(value)) // undefined

3.3 reject 和错误处理

const randomPromise = new Promise((resolve, reject) => {
  const number = Math.ceil(Math.random() * 10)

  if (number <= 5) {
    resolve(number)
  } else {
    reject(new Error(`数字太大:${number}`))
  }
})

randomPromise.then(
  value => console.log('resolved:', value),
  error => console.log('rejected:', error.message)
)

then 的第二个参数只处理前一个 Promise 的 rejection;它不会捕获同一个 then 的成功处理器中抛出的异常。链尾使用 catch 通常更清晰:

randomPromise
  .then(value => {
    console.log(value)
    return processValue(value)
  })
  .catch(error => {
    console.error('请求或处理失败:', error)
  })

function processValue(value) {
  return value * 2
}

catch(onRejected) 等价于 then(undefined, onRejected)

promise.catch(onRejected)
// 等价于
promise.then(undefined, onRejected)

如果成功处理器返回 new Error(),那是 fulfilled 的普通值;只有 throw new Error()return Promise.reject(error) 才会进入 rejected:

Promise.resolve()
  .then(() => new Error('普通返回值'))
  .then(value => console.log(value instanceof Error)) // true

Promise.resolve()
  .then(() => { throw new Error('真正拒绝') })
  .catch(error => console.log(error.message)) // 真正拒绝

3.4 finally

finally 不接收成功值或拒绝原因,通常用于无论成功失败都要执行的清理:

fetch('/api/data')
  .then(response => response.json())
  .catch(error => {
    console.error(error)
    throw error
  })
  .finally(() => {
    console.log('hide loading indicator')
  })

如果 finally 回调抛错或返回 rejected Promise,它会覆盖前面的结果;否则原来的值或原因会继续向后传递。

四、Promise 的并发组合

4.1 Promise.all

Promise.all 接收可迭代对象,全部 fulfilled 才 fulfilled;任意一个 rejected 就 rejected。结果数组的顺序与输入顺序一致,不与完成先后有关:

const userPromise = fetch('/api/user').then(response => response.json())
const settingsPromise = fetch('/api/settings').then(response => response.json())

Promise.all([userPromise, settingsPromise])
  .then(([user, settings]) => {
    console.log(user, settings)
  })
  .catch(error => {
    console.error('任一请求失败:', error)
  })

空数组的结果是立即 fulfilled 的空数组:

Promise.all([]).then(values => console.log(values)) // []

原文用于说明资源并行加载的思路仍然成立,但 Flash 已过时;现代场景可以是图片、字体、接口或模块资源。

4.2 Promise.race

Promise.race 在输入中第一个 settled 的 Promise 出现时结束:第一个 fulfilled 则成功,第一个 rejected 则失败。它不会取消其他任务:

function delay(value, milliseconds) {
  return new Promise(resolve => {
    setTimeout(() => resolve(value), milliseconds)
  })
}

Promise.race([
  delay('slow', 100),
  delay('fast', 10)
]).then(console.log) // fast

原文通过图片请求和超时 Promise 演示 race

function requestImage(url) {
  return new Promise((resolve, reject) => {
    const image = new Image()
    image.onload = () => resolve(image)
    image.onerror = () => reject(new Error('图片加载失败'))
    image.src = url
  })
}

function timeout(milliseconds) {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('图片请求超时')), milliseconds)
  })
}

Promise.race([
  requestImage('/assets/image.png'),
  timeout(5000)
]).then(
  image => console.log(image),
  error => console.error(error.message)
)

如果需要真正取消 fetch,应使用 AbortController

async function fetchWithTimeout(url, milliseconds = 5000) {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), milliseconds)

  try {
    const response = await fetch(url, { signal: controller.signal })
    return response
  } finally {
    clearTimeout(timer)
  }
}

4.3 其他静态方法

const tasks = [
  Promise.resolve('success'),
  Promise.reject(new Error('failed'))
]

Promise.allSettled(tasks).then(console.log)
// [{ status: 'fulfilled', value: 'success' },
//  { status: 'rejected', reason: Error('failed') }]

Promise.any([
  Promise.reject(new Error('first failure')),
  Promise.resolve('first success')
]).then(console.log) // first success
  • Promise.allSettled:等待全部结束,不因单项失败而提前拒绝;
  • Promise.any:第一个成功就成功,全部失败时以 AggregateError 拒绝;
  • Promise.resolve:创建/采纳一个 fulfilled 或 thenable 结果;
  • Promise.reject:创建 rejected Promise。

五、原文插图

原文的 Promise 状态和 race 演示图已下载到本地:

Promise 状态演示图一

图片来源:原始图片

Promise 状态演示图二

图片来源:原始图片

Promise catch 演示图

图片来源:原始图片

Promise race 演示图

图片来源:原始图片

六、手写 Promise:先实现状态和回调队列

手写 Promise 的目标不是替换原生实现,而是理解以下机制:

  1. executor 同步执行;
  2. 状态只能完成一次;
  3. pending 时暂存成功/失败处理器;
  4. settled 后按顺序调度处理器;
  5. then 返回新的 Promise;
  6. 处理函数的结果通过解析过程传给新 Promise。

原文第一版代码中的等待状态拼写不一致,应统一为 pending。此外,真正的 Promise 处理器应异步调度;只在状态改变时同步 forEach 回调,会与原生 Promise 语义不同。

6.1 Promise/A+ 解析过程

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError('Promise cannot resolve to itself'))
    return
  }

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called = false
    let then

    try {
      then = x.then
    } catch (error) {
      reject(error)
      return
    }

    if (typeof then === 'function') {
      try {
        then.call(
          x,
          value => {
            if (called) return
            called = true
            resolvePromise(promise2, value, resolve, reject)
          },
          reason => {
            if (called) return
            called = true
            reject(reason)
          }
        )
      } catch (error) {
        if (called) return
        called = true
        reject(error)
      }
      return
    }
  }

  resolve(x)
}

这里的 called 很重要:不可信 thenable 可能同时调用成功和失败回调,也可能多次调用;解析过程只接受第一次结果。读取 x.then 也可能触发 getter 并抛错,因此必须捕获异常。

6.2 一个可运行的 MyPromise

下面的实现保留原文的类、状态数组、链式 thencatchallrace 和 A+ deferred 思路,并修正状态一次性、thenable、空数组、异常和自引用问题。它用于学习,不是完整的原生 Promise polyfill:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.handlers = []

    let alreadyResolved = false

    const fulfill = value => {
      if (this.status !== PENDING) return
      this.status = FULFILLED
      this.value = value
      this.flush()
    }

    const rejectInternal = reason => {
      if (this.status !== PENDING) return
      this.status = REJECTED
      this.reason = reason
      this.flush()
    }

    const resolveValue = value => {
      if (value === this) {
        rejectInternal(new TypeError('Promise cannot resolve to itself'))
        return
      }

      if (value !== null &&
        (typeof value === 'object' || typeof value === 'function')) {
        let then
        try {
          then = value.then
        } catch (error) {
          rejectInternal(error)
          return
        }

        if (typeof then === 'function') {
          let called = false
          try {
            then.call(
              value,
              nextValue => {
                if (called) return
                called = true
                resolveValue(nextValue)
              },
              reason => {
                if (called) return
                called = true
                rejectInternal(reason)
              }
            )
          } catch (error) {
            if (!called) {
              called = true
              rejectInternal(error)
            }
          }
          return
        }
      }

      fulfill(value)
    }

    const resolve = value => {
      if (alreadyResolved) return
      alreadyResolved = true
      resolveValue(value)
    }

    const reject = reason => {
      if (alreadyResolved) return
      alreadyResolved = true
      rejectInternal(reason)
    }

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

  flush() {
    const handlers = this.handlers.splice(0)
    for (const handler of handlers) {
      queueMicrotask(handler)
    }
  }

  then(onFulfilled, onRejected) {
    const fulfilled = typeof onFulfilled === 'function'
      ? onFulfilled
      : value => value
    const rejected = typeof onRejected === 'function'
      ? onRejected
      : reason => { throw reason }

    let resolveNext
    let rejectNext
    const promise2 = new MyPromise((resolve, reject) => {
      resolveNext = resolve
      rejectNext = reject
    })

    const run = () => {
      try {
        const callback = this.status === FULFILLED ? fulfilled : rejected
        const input = this.status === FULFILLED ? this.value : this.reason
        resolveNext(callback(input))
      } catch (error) {
        rejectNext(error)
      }
    }

    if (this.status === PENDING) {
      this.handlers.push(run)
    } else {
      queueMicrotask(run)
    }

    return promise2
  }

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

  finally(onFinally) {
    if (typeof onFinally !== 'function') return this.then()

    return this.then(
      value => MyPromise.resolve(onFinally()).then(() => value),
      reason => MyPromise.resolve(onFinally()).then(() => {
        throw reason
      })
    )
  }

  static resolve(value) {
    return value instanceof MyPromise
      ? value
      : new MyPromise(resolve => resolve(value))
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

  static all(iterable) {
    const values = Array.from(iterable)

    return new MyPromise((resolve, reject) => {
      if (values.length === 0) {
        resolve([])
        return
      }

      const result = new Array(values.length)
      let remaining = values.length

      values.forEach((value, index) => {
        MyPromise.resolve(value).then(item => {
          result[index] = item
          remaining -= 1
          if (remaining === 0) resolve(result)
        }, reject)
      })
    })
  }

  static race(iterable) {
    return new MyPromise((resolve, reject) => {
      for (const value of iterable) {
        MyPromise.resolve(value).then(resolve, reject)
      }
    })
  }

  static deferred() {
    const deferred = {}
    deferred.promise = new MyPromise((resolve, reject) => {
      deferred.resolve = resolve
      deferred.reject = reject
    })
    return deferred
  }
}

6.3 使用和自测

MyPromise.resolve(1)
  .then(value => value + 1)
  .then(value => console.log(value)) // 2

MyPromise.all([
  MyPromise.resolve('a'),
  2,
  new MyPromise(resolve => setTimeout(() => resolve('c'), 10))
]).then(values => console.log(values)) // ['a', 2, 'c']

const deferred = MyPromise.deferred()
deferred.promise.then(value => console.log(value))
deferred.resolve('deferred value')

可以用以下边界检查实现:

const thenable = {
  then(resolve, reject) {
    resolve(1)
    reject(new Error('ignored'))
  }
}

MyPromise.resolve(thenable).then(value => console.log(value)) // 1

let self
self = MyPromise.resolve().then(() => self)
self.catch(error => console.log(error.name)) // TypeError

MyPromise.all([]).then(values => console.log(values)) // []

七、原文代码中的问题说明

原文抓取代码中有一些会导致语法错误或语义错误的片段,整理如下:

  • 等待状态的拼写与判断值不一致,应统一为 pending
  • 函数关键字、resolve 和变量名存在拼写错误,应统一修正;
  • 自引用判断应比较 promise2x
  • 静态方法调用应使用 MyPromise.resolve(item)
  • 自定义 Promise.resolve 的返回表达式缺少闭合括号;
  • Promise.all 必须处理普通值、空 iterable,并按输入索引保存结果;
  • Promise.race 也必须使用 Promise.resolve 采纳普通值和 thenable;
  • 仅用 instanceof Promise 不能支持其他实现或 thenable;
  • 直接在 resolve 时同步执行所有回调,不符合原生 Promise 的异步处理器语义;
  • 只在 pending 分支保存回调,还要保证已 settled 后新增的 then 也会异步执行;
  • Promise 的 resolving function 在第一次调用时就锁定,即使它正在等待一个 pending thenable;
  • 自定义实现不应命名为 Promise 覆盖全局原生 Promise,示例使用 MyPromise 更安全。

八、Promise、async/await 和错误传播

async 函数总是返回 Promise;await 会等待一个可被 Promise 解析的值:

async function loadData() {
  const response = await fetch('/api/data')
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`)
  }
  return response.json()
}

loadData()
  .then(data => console.log(data))
  .catch(error => console.error(error))

await 让链式逻辑看起来像同步代码,但不会阻塞线程;抛出的异常会让 async 函数返回的 Promise rejected。多个独立任务不要无意中串行等待:

async function loadPage() {
  const userPromise = fetch('/api/user').then(response => response.json())
  const settingsPromise = fetch('/api/settings').then(response => response.json())

  const [user, settings] = await Promise.all([userPromise, settingsPromise])
  return { user, settings }
}

九、总结

  • Promise 是异步结果的状态容器,但构造器 executor 会同步执行;
  • Promise 本身不能消除所有回调、竞态、取消和超时问题;
  • then 返回新 Promise,普通返回值、异常和 thenable 会决定下一环;
  • catch 用于统一处理链上的拒绝,finally 用于清理;
  • allraceallSettledany 适用于不同的并发策略;
  • race 只决定结果,不会自动取消输掉的任务;
  • 手写 Promise 的难点在状态一次性、异步回调、thenable 解析、自引用和异常处理;
  • Promise/A+ 主要规定 then 和解析过程,原生 Promise 还包括更多 ECMAScript 与宿主语义;
  • 生产代码优先使用原生 Promise、async/awaitAbortController,手写实现用于学习和测试。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS