前端八股(1)--Promise 常用方法有哪些?和async和await的区别

目录

  • [一、先搞懂:同步 / 异步](#一、先搞懂:同步 / 异步)
  • 1). 同步. 同步)
  • 2). 异步. 异步)
  • [二、Promise 到底是什么?](#二、Promise 到底是什么?)
  • 基础用法
  • [什么叫 JS 内置构造函数?](#什么叫 JS 内置构造函数?)
  • [1. 先懂什么是构造函数](#1. 先懂什么是构造函数)
  • [2. 什么叫 内置?](#2. 什么叫 内置?)
  • [3. 所以合起来:](#3. 所以合起来:)
  • [三、Promise 常用方法(面试必背 + 工作高频)](#三、Promise 常用方法(面试必背 + 工作高频))
  • [1. 实例方法(对象调用)](#1. 实例方法(对象调用))
  • [2. 静态方法(类直接调用)](#2. 静态方法(类直接调用))
  • Promise.allSettled():等待全部结束,成功失败都返回结果
  • [1. 实例方法(通过对象调用)](#1. 实例方法(通过对象调用))
  • [2. 静态方法(直接用类名调用)](#2. 静态方法(直接用类名调用))
  • 最简单记忆法
  • [场景 A:并发请求(最常用)](#场景 A:并发请求(最常用))
  • [场景 B:超时控制](#场景 B:超时控制)
  • [场景 C:批量上传图片](#场景 C:批量上传图片)
  • [场景 D:备用接口](#场景 D:备用接口)
  • [四、async/await 语法糖(Promise 的升级版)](#四、async/await 语法糖(Promise 的升级版))
  • 核心结论
  • [4 条使用规则](#4 条使用规则)
  • 对比写法(最直观)
  • [1)Promise 链式写法](#1)Promise 链式写法)
  • [2)async/await 写法(更优雅、更易读)](#2)async/await 写法(更优雅、更易读))
  • 执行顺序
  • [五、Promise vs async/await 区别(面试标准答案)](#五、Promise vs async/await 区别(面试标准答案))
  • [1. 什么是宏任务(普通任务)](#1. 什么是宏任务(普通任务))
  • [2. 什么是微任务(VIP 插队任务)](#2. 什么是微任务(VIP 插队任务))
  • [3. 执行顺序铁律(必须背)](#3. 执行顺序铁律(必须背))
  • [六、哪些东西会返回 Promise?(工作常用)](#六、哪些东西会返回 Promise?(工作常用))
  • 七、最容易混淆的点(一定要记住)
  • 总结
  • 手写题(高频)
  • [1. 手写 Promise.all](#1. 手写 Promise.all)
  • [2. 手写 Promise.race](#2. 手写 Promise.race)
  • [3. 手写 Promise 重复请求 / 重试(常考)](#3. 手写 Promise 重复请求 / 重试(常考))
  • 面试题
  • [1. Promise 状态有哪几种?](#1. Promise 状态有哪几种?)
  • [2. then 为什么能链式调用?](#2. then 为什么能链式调用?)
  • [3. Promise.all 和 allSettled 区别?](#3. Promise.all 和 allSettled 区别?)
  • [4. race 和 any 区别?](#4. race 和 any 区别?)
  • [5. async 函数返回什么?](#5. async 函数返回什么?)
  • [6. await 后面跟普通函数会怎样?](#6. await 后面跟普通函数会怎样?)
  • [7. Promise 中同步代码和微任务执行顺序?](#7. Promise 中同步代码和微任务执行顺序?)
  • [8. 手写 Promise.all(面试高频)](#8. 手写 Promise.all(面试高频))
  • [9. 如何中断 Promise?](#9. 如何中断 Promise?)
  • [10. 什么是宏任务 / 微任务?](#10. 什么是宏任务 / 微任务?)
  • 宏任务(macrotask)
  • 微任务(microtask)
  • 执行顺序铁律

一、先搞懂:同步 / 异步

1). 同步

代码从上到下按顺序执行,必须等上一行执行完毕,下一行才会运行。

  • 阻塞后续代码

2). 异步

代码不会等待,先继续执行后面的同步代码,等异步任务完成后再回头执行回调。

  • 不会阻塞代码
  • 常见异步任务:
    1. 定时器:setTimeoutsetInterval
    2. 网络请求:AJAX /fetch/axios
    3. 文件操作(Node.js)
    4. Promise、async/await

二、Promise 到底是什么?

  1. JS 内置构造函数,专门处理异步操作
  2. 核心作用:解决回调地狱(多层嵌套回调)
  3. 本质:存放异步未来结果的容器 (本身不是异步,.then / await 是异步)

基础用法

javascript 复制代码
// 1. 创建 Promise
const p = new Promise((resolve, reject) => {
  // 异步任务:定时器、ajax、fetch...
  setTimeout(() => {
    resolve("成功数据"); // 成功 → 触发 then
    // reject("失败原因"); // 失败 → 触发 catch
  }, 1000);
});

// 2. 使用 Promise
p.then(res => {
  console.log(res); // 成功
}).catch(err => {
  console.log(err); // 失败
}).finally(() => {
  console.log("无论成功失败都执行"); // 清理工作
});

什么叫 JS 内置构造函数

一句话:JS 语言自带的、用来造东西的 "工厂函数",就叫内置构造函数。


1. 先懂什么是构造函数

构造函数 = 用来创建对象的函数特点:

  • 名字一般首字母大写
  • 必须用 new 来调用
  • 调用后会返回一个新对象

比如:

复制代码
new Promise()
new Array()
new Object()
new Date()

这些都是构造函数。


2. 什么叫 内置

内置 = JS 引擎自带,不用你引入,不用你定义,直接就能用。

不是你写的,不是库带的,是浏览器 / Node 天生就有的


3. 所以合起来:

JS 内置构造函数 = JS 自带的、用 new 来创建对象的函数


三、Promise 常用方法(面试必背 + 工作高频

1. 实例方法(对象调用)

  • .then():异步成功时执行
  • .catch():异步失败时执行
  • .finally():无论成功 / 失败都执行(关闭加载、清理资源)

2. 静态方法(类直接调用)

  1. Promise.resolve():快速创建成功的 Promise
  2. Promise.reject():快速创建失败的 Promise
  3. Promise.all()等待全部成功,一个失败 → 整体失败
  4. Promise.race()谁先完成就返回谁(不管成功失败)

Promise.allSettled()等待全部结束,成功失败都返回结果

  1. Promise.any()只要一个成功就返回,全部失败才失败

1. 实例方法(通过对象调用)

先 new 一个 Promise 对象,再调用:

javascript 复制代码
const p = new Promise(...)

p.then(...)
p.catch(...)
p.finally(...)
  • then / catch / finally
  • 必须先有 Promise 对象才能用
  • 实例方法

2. 静态方法(直接用类名调用)

不需要 new,直接用 Promise.xxx ()

javascript 复制代码
Promise.all(...)
Promise.race(...)
Promise.resolve(...)
Promise.reject(...)
Promise.allSettled(...)
Promise.any(...)
  • 直接用 Promise 这个名字点出来
  • 不需要创建对象
  • 这就叫静态方法

最简单记忆法

  • 对象。方法 → 实例方法
  • 类名。方法 → 静态方法
方法名 别名(外号) 成功条件 失败条件 返回结果 适用场景
Promise.resolve 成功工厂 立即成功 - 成功的 Promise 快速包装值、跳过异步流程
Promise.reject 失败工厂 - 立即失败 失败的 Promise 主动抛出错误、统一错误处理
Promise.all 全部都要 全部成功 一个失败 结果数组(顺序对应) 并行加载多个资源,做批量初始化
Promise.race 谁抢得快 第一个完成(成功) 第一个完成(失败) 第一个完成的值 超时控制、请求竞态、抢最快接口
Promise.allSettled 全部过完 无论成功失败 - 结果数组(含状态) 不关心成败,只需要全部处理完
Promise.any 只要一个赢 一个成功 全部失败 第一个成功的值 失败也没关系,只要有一个能用就好
场景 A:并发请求(最常用)

需求:一次性拿用户信息、订单、购物车数据,要等全部拿到才能渲染页面。

javascript 复制代码
// 只要有一个挂了,直接进 catch
Promise.all([fetchUserInfo(), fetchOrders(), fetchCart()])
  .then(([user, orders, cart]) => { ... })
  • 策略:同时发三个请求,等它们全部返回成功,再渲染页面。
  • 优势:总耗时 = 最慢的那个接口时间(不是时间之和),性能最快。

场景 B:超时控制

需求:接口如果 3 秒没返回,就报错。

javascript 复制代码
// race 谁先赢算谁的
Promise.race([
  fetchApi(), 
  new Promise((_, reject) => setTimeout(() => reject('超时'), 3000))
])
  • 场景:支付接口如果 3 秒没返回,就自动超时报错,不要一直等。
  • 策略 :把接口请求和定时器比赛放在 race 里,谁先跑完(定时器或接口),谁就赢。

场景 C:批量上传图片

需求:上传 10 张图,有的图可能失败,有的成功,最后要把 10 张的状态全部记录下来。

javascript 复制代码
// 不怕失败,全部跑完再汇报
Promise.allSettled([uploadImg1(), uploadImg2()])
  .then(results => { 
    // results 里包含每个图片的 { status, reason/value }
  })
  • 场景:发朋友圈要上传 10 张图。
  • 策略
    • 不能因为 1 张图传失败了,就把其他 9 张删掉(不使用 all)。
    • 也不需要最快的(不需要 race)。
    • 只需要全部上传完,记录下哪些成功了,哪些失败了,最后给用户一个汇总提示。

场景 D:备用接口

需求:A 接口挂了,立马用 B 接口,只要有一个能用就行。

javascript 复制代码
// 只要有一个成功就返回
Promise.any([fetchApiV1(), fetchApiV2()])
  .then(res => { console.log('使用了可用的那个接口', res) })
  • 场景:调用主接口 A 失败了,自动切接口 B。只要有一个能用就继续。
  • 区别 :比 race 更智能。race 第一个失败就停了,any只要有一个成功就停,全部失败才报错。

resolve /reject ------ 造 Promise 的工具

它们的作用只有一个:快速创建一个已经成功 / 失败的 Promise 对象。

什么时候用?

  • 你想让一个函数立即返回一个结果
  • 你想把一个普通值(如字符串、数字)变成 Promise
  • 做缓存、做中断、做快速返回

all /race/allSettled /any ------ 协调多个 Promise 的指挥官

这些函数处理的是一组 Promise 数组,用来控制多个异步任务的执行顺序、结果收集等。


四、async/await 语法糖(Promise 的升级版)

核心结论

async/await 是 Promise 的语法糖,没有替代 Promise,只是让异步代码写起来像同步代码。

4 条使用规则

  1. async 必须写在函数前面
  2. await 只能在 async 函数内部使用
  3. await 后面必须跟 Promise 对象
  4. 错误处理必须用 try/catch

对比写法(最直观)

1)Promise 链式写法
javascript 复制代码
function getData() {
  fetch("/api")
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.log(err));
}
2)async/await 写法(更优雅、更易读)
javascript 复制代码
async function getData() {
  try {
    const res = await fetch("/api"); // 等待 Promise 完成
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.log(err); // 捕获错误
  }
}

await 后面 = 整段 "被暂停的代码" 只要在 await 这一行的下面 ,都属于:await 等待完成后,才会执行的代码

javascript 复制代码
async function foo() {
  console.log('1'); // 立即执行(同步)

  // ↓↓↓ 遇到 await,函数在这里暂停 ↓↓↓
  await new Promise(resolve => {
    setTimeout(() => {
      resolve(); 
    }, 2000);
  });
  // ↑↑↑ 等待 Promise 成功 ↑↑↑

  // ======================================
  // 👇👇👇 【这一整块】都是 await 后面的代码 👇👇👇
  console.log('2'); 
  console.log('别的代码');
  function test() {}
  // 👆👆👆 【全部都要等 await 结束】
  // ======================================
}

foo();
console.log('3');

执行顺序

  1. 打印 1
  2. 遇到 await暂停 foo 函数
  3. 跳出函数 → 打印 3
  4. 等 2 秒 → Promise 成功
  5. 恢复执行 foo 函数
  6. 打印 2

await = 暂停 + 等待 + 恢复

  1. 遇到 await 函数立刻暂停,下面所有代码都冻结
  2. 等待 等后面的 Promise 变成 成功(resolve)
  3. 恢复执行await 下面所有代码 当作微任务继续执行

五、Promise vs async/await 区别(面试标准答案

对比项 Promise async/await
本质 异步处理对象、解决回调地狱 Promise 的语法糖
写法 .then() 链式调用 同步风格代码
可读性 多层链式相对繁琐 更清晰、接近同步代码
错误处理 .catch() try/catch
依赖关系 独立存在 必须依赖 Promise
执行顺序 基于微任务异步 同步写法,底层还是异步

**一句话总结:**Promise 是基础,async/await 是让 Promise 更好用、更易读的语法糖。

1. 什么是宏任务(普通任务)

可以理解成:排队的普通任务

常见宏任务:

  • setTimeoutsetInterval
  • 网络请求(ajax/fetch/axios)
  • I/O、文件操作
  • DOM 事件(click、scroll)
  • UI 渲染

特点:

  • 每次事件循环只执行一个
  • 执行完一个,就去检查微任务

2. 什么是微任务(VIP 插队任务)

可以理解成:优先执行的插队任务

常见微任务:

  • Promise.then / catch / finally
  • async/await 后面的代码
  • queueMicrotask
  • MutationObserver

特点:

  • 一次性全部清空
  • 优先级比宏任务高得多

3. 执行顺序铁律(必须背)

  1. 先执行所有同步代码
  2. 执行当前所有微任务(一口气清空)
  3. 再执行一个宏任务
  4. 再清空微任务
  5. 再执行下一个宏任务...... 循环

六、哪些东西会返回 Promise?(工作常用)

  1. 手动 return new Promise(...)
  2. async 函数(自动包装成 Promise)
  3. 常用库 / API:
    • fetch()
    • axios.get()
    • Node.js:fs.promisesmysql2/promise
  4. Promise 静态方法:resolve/reject/all/race

七、最容易混淆的点(一定要记住)

  1. Promise 本身不是异步,它只是装异步结果的容器
  2. .then()await 一定是异步(微任务)
  3. async/await 不能脱离 Promise 单独使用
  4. 异步代码不会阻塞同步代码执行
  • async 函数 = 一定返回 Promise = 一定是异步不管里面有没有 await,都异步。

  • **await = 只是在函数内部 "等待并取出结果 "**不写 await,函数照样异步,只是不等待。

  • 异步与否,看是不是 Promise,不看有没有 await

  • 在函数内部拿结果 → 用 await
  • 返回 Promise 给外面 → 不用 await
  • 只要是 async 函数永远异步,跟写不写 await 无关

总结

  1. 同步阻塞、异步不阻塞,Promise 专门解决异步回调地狱
  2. Promise 方法:then/catch/finally(实例) + all/race/allSettled/any(静态)
  3. async/await = Promise 语法糖,同步写法,异步效果,错误用 try/catch
  4. 所有 async/await 底层都是 Promise,二者相辅相成

await 后面是 Promise → 等它 resolve 后,再把后面代码放进微任务 await 后面不是 Promise → 直接把后面代码放进微任务

而且前面还有一个共同前提:

await 紧跟的那一行代码,永远同步立即执行


手写题(高频)

1. 手写 Promise.all

javascript 复制代码
function all(promises) {
  let results = []
  let count = 0
  return new Promise((resolve, reject) => {
    promises.forEach((p, i) => {
      Promise.resolve(p).then(res => {
        results[i] = res
        count++
        if (count === promises.length) resolve(results)
      }).catch(reject)
    })
  })
}

2. 手写 Promise.race

javascript 复制代码
function race(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach(p => {
      Promise.resolve(p).then(resolve).catch(reject)
    })
  })
}

3. 手写 Promise 重复请求 / 重试(常考)

javascript 复制代码
function retry(fn, times) {
  return fn().catch(err => {
    if (times > 0) return retry(fn, times - 1)
    throw err
  })
}

面试题

1. Promise 状态有哪几种?

三种状态:

  • pending:进行中(初始状态)
  • fulfilled:已成功
  • rejected:已失败

特点:

  • 状态一旦从 pending 变为 fulfilled /rejected,就不可逆
  • 状态改变后,结果会被永久保存

2. then 为什么能链式调用?

因为 .then() 会返回一个新的 Promise ,所以可以继续 .then().then() 链式调用。

返回规则:

  • return 普通值 → 包装成 Promise.resolve(值)
  • return Promise → 沿用这个 Promise
  • 抛错 → 变成 Promise.reject(错误)

3. Promise.all 和 allSettled 区别?

  • Promise.all

    • 必须全部成功才成功
    • 有一个失败 → 整体立即失败
    • 适合:必须全部成功才能继续的场景
  • Promise.allSettled

    • 等待全部结束,不管成功失败
    • 永远不会走到 catch
    • 返回数组:{ status: 'fulfilled'/'rejected', value/reason }
    • 适合:批量上传、批量请求,要统计结果

4. race 和 any 区别?

  • Promise.race

    • 第一个完成就返回谁
    • 第一个失败 → 整体失败
    • 常用于:超时控制
  • Promise.any

    • 第一个成功就返回谁
    • 失败会被忽略,直到出现成功
    • 全部失败才整体失败
    • 适合:多接口备用,只要一个能用就行

5. async 函数返回什么?

一定返回 Promise,不管你写不写 return。

规则:

  • return 普通值 → 包装成 Promise.resolve(值)
  • return Promise → 直接返回它
  • 抛错 → 变成 Promise.reject(错误)

6. await 后面跟普通函数会怎样?

  • 立即同步执行这个普通函数
  • 返回值会被包装成 Promise.resolve(返回值)
  • await 下面的代码进入微任务,同步代码跑完再执行
  • 不会真正等待异步,效果接近同步

7. Promise 中同步代码和微任务执行顺序?

  • new Promise((resolve) => { ... }) 内部代码 立即同步执行
  • .then() / .catch() 里的回调是微任务
  • 执行顺序:同步代码 > 微任务 > 宏任务

8. 手写 Promise.all(面试高频)

javascript 复制代码
function PromiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) return reject(new Error('不是数组'))

    const result = []
    let count = 0
    const len = promises.length

    if (len === 0) return resolve([])

    promises.forEach((p, index) => {
      Promise.resolve(p).then(res => {
        result[index] = res
        count++
        if (count === len) resolve(result)
      }).catch(err => {
        reject(err)
      })
    })
  })
}

9. 如何中断 Promise?

Promise 本身不能取消,但可以模拟中断:

  1. 用 Promise.race 做超时中断
javascript 复制代码
function withAbort(promise, timeout) {
  const abort = new Promise((_, reject) => {
    setTimeout(() => reject('abort'), timeout)
  })
  return Promise.race([promise, abort])
}
  1. 用标志位跳过逻辑
javascript 复制代码
let aborted = false
async function fn() {
  if (aborted) return
  await request()
}
aborted = true // 中断
  1. 新标准:AbortController(fetch 可用)
javascript 复制代码
const controller = new AbortController()
fetch('/api', { signal: controller.signal })
controller.abort() // 直接中断

10. 什么是宏任务 / 微任务?

宏任务(macrotask)

  • 优先级低,每次执行一个
  • 常见:
    • setTimeout / setInterval
    • ajax / fetch
    • DOM 事件、UI 渲染

微任务(microtask)

  • 优先级高,一次性清空队列
  • 常见:
    • Promise.then/catch/finally
    • async/await 后面代码
    • queueMicrotask
    • MutationObserver

执行顺序铁律

同步代码 → 微任务(清空) → 宏任务(一个) → 循环

相关推荐
喵个咪2 小时前
Go 语言 CMS 横评:风行 GoWind 对比传统 PHP/Java CMS 核心优势
前端·后端·cms
落魄江湖行2 小时前
入门篇八 Nuxt4页面元信息与 SEO:让搜索引擎爱上你的网站
前端·typescript·seo·nuxt4
╰つ栺尖篴夢ゞ2 小时前
Web之深入解析Cookie的安全防御与跨域实践
前端·安全·存储·cookie·跨域
木斯佳2 小时前
前端八股文面经大全:腾讯前端一面(2026-04-04)·深度解析
前端·ai·鉴权·monorepo
code_Bo2 小时前
kiro生成小程序商业案例
前端·微信小程序·小程序·云开发
yellowbuff2 小时前
为什么你的 0.01 秒倒计时看起来一卡一卡的?
前端
onebyte8bits2 小时前
NestJS 系列教程(十八):文件上传与对象存储架构(Multer + S3/OSS + 访问控制)
前端·架构·node.js·状态模式·nestjs
Ruihong2 小时前
放弃 Vue3 传统 <script>!我的 VuReact 编译器做了一次清醒取舍
前端·vue.js
weixin_456164832 小时前
vue3 父组件向子组件传参
前端