23种Promise高效用法,彻底搞定前端异步痛点,新手也能秒上手

16 阅读20分钟

前端人必看!你是不是也被异步操作逼疯过😭

回调地狱嵌套一层又一层,代码乱得像“蜘蛛网”;并发请求太多导致接口拥堵,页面卡顿崩溃;异步请求超时、无法取消,报错无从排查;不知道怎么顺序/并行执行异步任务,越写越乱……

其实这些前端异步痛点,Promise早就给出了完美解决方案!作为JS异步编程的核心,Promise不仅能替代繁琐的回调函数,还能优雅处理并发、超时、重试等复杂场景,是前端工程师必备的核心技能,也是面试高频考点。

今天这篇文章,结合实战场景,把23种Promise高效用法逐一拆解,每一种都配「核心作用+完整可复制代码+通俗解读+实战场景」,从基础到进阶,小白能看懂,老手能查漏补缺,收藏这一篇,再也不用东拼西凑找资料,异步开发效率直接翻倍!

核心提醒:Promise的核心是“异步操作的状态管理”,三种状态(pending/fulfilled/rejected)一旦改变就无法逆转,掌握它的核心用法,能解决80%的前端异步问题,剩下20%靠这23种实战技巧搞定!

一、基础核心用法(3种)—— 入门必备,筑牢基础

这3种是Promise最基础、最常用的用法,是后续高级用法的前提,新手务必先掌握,熟练运用就能告别基础异步烦恼。

1. 使用async/await简化Promise(最常用)

核心作用:用同步代码的写法,实现异步操作,彻底告别回调地狱,代码逻辑更清晰、更易维护,是日常开发中最推荐的异步写法。

通俗解读:async关键字标记函数为异步函数,await关键字“等待”异步操作完成,只有await后面的异步操作结束,才会执行下一行代码,避免嵌套。

// 基础用法:处理单个异步请求
async function asyncFunction() {
  try {
    // 等待异步操作完成(比如接口请求)
    const result = await fetch('/api/data');
    const data = await result.json();
    // 异步操作成功后,执行后续逻辑
    console.log('接口请求成功:', data);
    return data;
  } catch (error) {
    // 捕获异步操作中的所有错误
    console.error('接口请求失败:', error);
  }
}

// 实战用法:结合业务场景(获取用户信息并渲染)
async function getUserInfo() {
  try {
    const response = await fetch('/api/user/info');
    const user = await response.json();
    // 渲染用户信息到页面
    document.querySelector('.user-name').textContent = user.name;
    document.querySelector('.user-age').textContent = user.age;
  } catch (err) {
    // 错误处理(比如提示用户获取失败)
    alert('获取用户信息失败,请重试!');
  }
}
// 调用异步函数
getUserInfo();

2. 使用Promises替代回调函数

核心作用:将传统的回调函数(回调地狱的根源),转化为Promise链式调用,代码更优雅,错误处理更统一。

通俗解读:很多老项目或原生API仍使用回调函数(比如setTimeout、fs.readFile),通过封装,将其转化为Promise,就能享受链式调用的便捷。

// 核心封装:将回调函数转为Promise
const callbackToPromise = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    // 执行原回调函数,在回调中触发Promise的resolve/reject
    fn(...args, (error, result) => {
      if (error) {
        // 回调报错,触发reject
        reject(error);
      } else {
        // 回调成功,触发resolve
        resolve(result);
      }
    });
  });
};

// 实战用法:封装setTimeout(回调转Promise)
const delay = callbackToPromise(setTimeout, 1000);
// 链式调用,替代回调嵌套
delay.then(() => {
  console.log('1秒后执行');
  return callbackToPromise(setTimeout, 2000);
}).then(() => {
  console.log('再等2秒执行');
}).catch(err => {
  console.error('出错了:', err);
});

3. 使用Promise实现一个延时函数

核心作用:异步延时操作,常用于防抖、节流、模拟接口请求延迟等场景,比原生setTimeout更灵活,可结合await使用。

// 基础封装:异步延时函数
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

// 实战用法1:结合await使用(同步写法实现延时)
async function delayDemo() {
  console.log('开始执行');
  await delay(1500); // 延时1.5秒
  console.log('1.5秒后执行');
  await delay(2000); // 再延时2秒
  console.log('总共延时3.5秒后执行');
}

// 实战用法2:模拟接口请求延迟
async function mockFetch() {
  console.log('正在请求接口...');
  // 模拟接口延迟1秒
  await delay(1000);
  return { code: 200, data: '模拟接口返回数据', msg: 'success' };
}

mockFetch().then(res => console.log(res)).catch(err => console.error(err));

二、并发控制用法(4种)—— 解决多异步拥堵,提升性能

日常开发中,经常需要同时执行多个异步请求(比如批量获取数据),但并发过多会导致接口拥堵、页面卡顿,这4种用法能优雅控制并发,兼顾效率和性能。

4. 映射并发Promises并处理结果数组(Promise.all)

核心作用:并行执行多个Promise,等待所有Promise都成功(fulfilled)后,返回所有结果的数组;只要有一个失败(rejected),就立即触发catch,适合“所有异步操作都必须成功”的场景。

// 基础用法:并发请求多个接口
const fetchUrls = urls => {
  // 映射所有url为fetch Promise
  const fetchPromises = urls.map(url => 
    fetch(url).then(response => response.json())
  );
  // 等待所有Promise执行完成
  return Promise.all(fetchPromises);
};

// 实战用法:批量获取用户列表、商品列表、分类列表
const urls = [
  '/api/users',
  '/api/goods',
  '/api/categories'
];

fetchUrls(urls).then(results => {
  const [users, goods, categories] = results;
  console.log('用户列表:', users);
  console.log('商品列表:', goods);
  console.log('分类列表:', categories);
  // 所有数据获取完成后,渲染页面
}).catch(error => {
  console.error('某个接口请求失败:', error);
});

5. 并发控制(限制并发数量)

核心作用:Promise.all会同时执行所有Promise,当并发数量过多(比如几十上百个请求),会给服务器造成压力,通过此方法可限制同时执行的Promise数量,避免拥堵。

// 核心封装:并发控制函数
const concurrentPromises = (promises, limit) => {
  return new Promise((resolve, reject) => {
    let i = 0; // 当前执行到的Promise索引
    let result = []; // 存储所有Promise的结果
    // 执行单个Promise的函数
    const executor = () => {
      // 所有Promise都执行完成,返回结果
      if (i >= promises.length) {
        return resolve(result);
      }
      // 取出当前要执行的Promise
      const promise = promises[i++];
      // 执行Promise并处理结果
      Promise.resolve(promise)
        .then(value => {
          result.push(value);
          // 继续执行下一个Promise(递归调用)
          if (i < promises.length) {
            executor();
          } else {
            // 最后一个Promise执行完成,返回结果
            resolve(result);
          }
        })
        .catch(reject); // 任意一个Promise失败,直接触发reject
    };

    // 初始化,同时执行limit个Promise
    for (let j = 0; j < limit && j < promises.length; j++) {
      executor();
    }
  });
};

// 实战用法:限制最多同时执行3个请求
const allPromises = [
  fetch('/api/data1').then(res => res.json()),
  fetch('/api/data2').then(res => res.json()),
  fetch('/api/data3').then(res => res.json()),
  fetch('/api/data4').then(res => res.json()),
  fetch('/api/data5').then(res => res.json())
];

// 限制并发数量为3
concurrentPromises(allPromises, 3).then(results => {
  console.log('所有请求完成,结果:', results);
}).catch(err => {
  console.error('请求失败:', err);
});

6. 使用Promise.allSettled处理多个异步操作

核心作用:与Promise.all相反,无论每个Promise是成功还是失败,都会等待所有Promise执行完成,返回每个Promise的状态和结果,适合“不需要所有异步都成功”的场景(比如批量统计接口状态)。

// 基础用法:统计多个接口的执行状态
const promises = [
  fetch('/api/endpoint1'),
  fetch('/api/endpoint2'),
  fetch('/api/endpoint3') // 假设这个接口会失败
];

Promise.allSettled(promises).then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      // 成功的Promise,处理结果
      console.log(`接口${index + 1}请求成功:`, result.value);
    } else {
      // 失败的Promise,处理错误
      console.error(`接口${index + 1}请求失败:`, result.reason);
    }
  });
});

// 实战用法:批量上传文件,统计成功/失败数量
async function batchUpload(files) {
  const uploadPromises = files.map(file => 
    fetch('/api/upload', {
      method: 'POST',
      body: file
    }).then(res => res.json())
  );

  const results = await Promise.allSettled(uploadPromises);
  const successCount = results.filter(r => r.status === 'fulfilled').length;
  const failCount = results.filter(r => r.status === 'rejected').length;
  console.log(`上传完成:成功${successCount}个,失败${failCount}个`);
}

7. 处理多个Promises的最快响应(Promise.race)

核心作用:并行执行多个Promise,只关心“最快完成”的那个Promise的结果(无论成功还是失败),适合“取最快响应”的场景(比如多节点接口容错)。

// 基础用法:获取多个接口中最快的响应
const promises = [
  fetch('/api/node1').then(res => res.json()), // 节点1,响应时间约500ms
  fetch('/api/node2').then(res => res.json()), // 节点2,响应时间约300ms
  fetch('/api/node3').then(res => res.json())  // 节点3,响应时间约800ms
];

Promise.race(promises)
  .then(value => {
    console.log('最快响应的接口数据:', value); // 会输出节点2的数据
    // 使用最快的响应数据渲染页面,提升用户体验
  })
  .catch(reason => {
    console.error('最早失败的接口:', reason); // 若最快的接口失败,直接触发
  });

// 实战用法:多CDN资源加载,取最快的那个
const loadResource = (urls) => {
  const loadPromises = urls.map(url => 
    new Promise((resolve) => {
      const img = new Image();
      img.onload = () => resolve(url);
      img.src = url;
    })
  );
  return Promise.race(loadPromises);
};

// 加载3个CDN的图片,取最快加载完成的
const imgUrls = [
  'https://cdn1.example.com/img.jpg',
  'https://cdn2.example.com/img.jpg',
  'https://cdn3.example.com/img.jpg'
];

loadResource(imgUrls).then(fastUrl => {
  console.log('最快加载的图片CDN:', fastUrl);
  document.querySelector('.img').src = fastUrl;
});

三、异常与状态处理用法(5种)—— 规避异步报错,提升代码健壮性

异步操作中,报错、超时、状态未知是常见问题,这5种用法能优雅处理这些异常,避免页面崩溃,让代码更健壮、更易维护。

8. Promise超时处理

核心作用:给Promise设置超时时间,若超过指定时间仍未完成(未resolve/reject),则自动reject,避免异步操作“卡死”,提升用户体验。

// 核心封装:带超时的Promise
const promiseWithTimeout = (promise, ms) =>
  Promise.race([
    promise, // 原异步操作
    // 超时定时器,超过ms毫秒后reject
    new Promise((resolve, reject) =>
      setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
    )
  ]);

// 实战用法:给接口请求设置3秒超时
async function fetchWithTimeout(url, ms = 3000) {
  try {
    const response = await promiseWithTimeout(fetch(url), ms);
    return await response.json();
  } catch (error) {
    if (error.message.includes('Timeout')) {
      // 处理超时逻辑(提示用户、重试等)
      alert('接口请求超时,请检查网络后重试!');
    } else {
      console.error('接口请求失败:', error);
    }
  }
}

// 调用:3秒内未响应则超时
fetchWithTimeout('/api/data', 3000);

9. Promise的取消

核心作用:JavaScript原生Promise无法直接取消,但通过封装,可模拟取消逻辑(比如用户切换页面、取消请求),避免无效异步操作浪费资源。

// 核心封装:可取消的Promise
const cancellablePromise = promise => {
  let isCanceled = false; // 取消标记

  // 包装原Promise,添加取消逻辑
  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      value => {
        // 若已取消,触发reject(携带取消标记)
        isCanceled ? reject({ isCanceled, value }) : resolve(value);
      },
      error => {
        // 若已取消,触发reject(携带取消标记)
        isCanceled ? reject({ isCanceled, error }) : reject(error);
      }
    );
  });

  // 返回Promise和取消方法
  return {
    promise: wrappedPromise,
    cancel() {
      isCanceled = true; // 标记为已取消
    }
  };
};

// 实战用法:用户取消接口请求
const { promise, cancel } = cancellablePromise(
  fetch('/api/largeData').then(res => res.json())
);

// 监听取消按钮点击
document.querySelector('.cancel-btn').addEventListener('click', () => {
  cancel(); // 取消Promise
  console.log('请求已取消');
});

// 处理Promise结果
promise.then(data => {
  console.log('请求成功:', data);
}).catch(err => {
  if (err.isCanceled) {
    // 处理取消逻辑(不提示错误)
  } else {
    console.error('请求失败:', err);
  }
});

10. 检测Promise状态

核心作用:原生Promise不允许直接查询状态(pending/fulfilled/rejected),通过此方法可获取Promise的当前状态,用于调试或特殊业务逻辑。

// 核心封装:检测Promise状态
const reflectPromise = promise =>
  promise.then(
    value => ({ status: 'fulfilled', value }), // 成功:返回状态和结果
    error => ({ status: 'rejected', error })   // 失败:返回状态和错误
  );

// 实战用法:检测多个Promise的状态
const promise1 = fetch('/api/data1').then(res => res.json());
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 1000));

// 检测两个Promise的状态
Promise.all([reflectPromise(promise1), reflectPromise(promise2)]).then(results => {
  results.forEach((result, index) => {
    console.log(`Promise${index + 1}状态:`, result.status);
    if (result.status === 'fulfilled') {
      console.log('结果:', result.value);
    } else {
      console.log('错误:', result.error);
    }
  });
});

11. 确保Promise只解决一次

核心作用:避免因代码逻辑问题(比如重复调用resolve),导致Promise被多次解决(fulfilled),引发后续逻辑异常。

// 核心封装:确保只resolve一次的Promise
const onceResolvedPromise = executor => {
  let isResolved = false; // 标记是否已resolve
  return new Promise((resolve, reject) => {
    executor(
      value => {
        // 只有未resolve过,才执行resolve
        if (!isResolved) {
          isResolved = true;
          resolve(value);
        }
      },
      reject // reject可多次调用,不影响
    );
  });
};

// 实战用法:避免重复提交(比如表单提交)
const submitForm = (formData) => {
  return onceResolvedPromise((resolve, reject) => {
    // 模拟表单提交接口
    fetch('/api/submit', {
      method: 'POST',
      body: formData
    }).then(res => res.json())
      .then(data => {
        resolve(data);
        // 模拟重复调用resolve(无效)
        resolve('重复resolve');
      })
      .catch(reject);
  });
};

// 调用:即使内部重复resolve,也只执行一次
submitForm({ name: '张三' }).then(data => {
  console.log('提交成功:', data); // 只输出一次
}).catch(err => {
  console.error('提交失败:', err);
});

12. 同时执行多个异步任务并处理中途的失败

核心作用:类似Promise.allSettled,但更灵活,可自定义失败处理逻辑,适合“允许部分异步失败,同时处理成功和失败结果”的场景。

// 结合reflectPromise,处理部分失败的场景
const promises = [
  fetch('/api/data1').then(res => res.json()), // 成功
  fetch('/api/data2').then(res => res.json()), // 失败
  fetch('/api/data3').then(res => res.json())  // 成功
];

// 先通过reflectPromise获取所有状态,再分别处理
Promise.all(promises.map(reflectPromise)).then(results => {
  // 处理成功的结果
  const successResults = results.filter(r => r.status === 'fulfilled').map(r => r.value);
  console.log('成功的结果:', successResults);

  // 处理失败的结果(比如记录日志、提示用户)
  const failResults = results.filter(r => r.status === 'rejected').map(r => r.error);
  failResults.forEach((err, index) => {
    console.error(`任务${index + 1}失败:`, err);
    // 可选:失败重试
    // retryPromise(() => fetch(`/api/data${index + 2}`).then(res => res.json()), 3, 1000);
  });
});

四、流程控制用法(6种)—— 优雅管理异步流程,避免混乱

异步流程混乱是前端异步开发的常见问题,比如顺序执行、条件执行、动态执行等,这6种用法能帮你精准控制异步流程,让代码逻辑更清晰。

13. 顺序执行Promise数组

核心作用:按顺序执行一组Promise,前一个异步操作完成后,再执行下一个,适合“依赖前一个结果”的场景(比如分步提交、依赖接口)。

// 核心封装:顺序执行Promise数组
const sequencePromises = promises =>
  promises.reduce(
    (prev, next) => prev.then(() => next()), // 前一个完成,执行下一个
    Promise.resolve() // 初始值:一个已resolve的Promise
  );

// 实战用法:分步提交表单(先验证、再提交、最后提示)
const step1 = () => new Promise(resolve => {
  console.log('步骤1:验证表单');
  setTimeout(() => resolve(), 1000);
});

const step2 = () => new Promise(resolve => {
  console.log('步骤2:提交表单');
  setTimeout(() => resolve(), 1500);
});

const step3 = () => new Promise(resolve => {
  console.log('步骤3:提示提交成功');
  setTimeout(() => resolve(), 500);
});

// 顺序执行3个步骤
sequencePromises([step1, step2, step3]).then(() => {
  console.log('所有步骤执行完成');
});

14. 基于条件的Promise链

核心作用:根据条件判断,决定是否执行下一个Promise,适合“分支异步逻辑”(比如根据用户权限,决定是否获取敏感数据)。

// 核心封装:条件Promise链
const conditionalPromise = (conditionFn, promise) => 
  conditionFn() ? promise : Promise.resolve(); // 条件不满足,返回已resolve的Promise

// 实战用法:根据用户权限,决定是否获取敏感数据
// 模拟判断用户是否有权限
const hasPermission = () => {
  const user = JSON.parse(localStorage.getItem('user'));
  return user?.role === 'admin'; // 管理员有权限
};

// 敏感数据接口(只有管理员能获取)
const fetchSensitiveData = () => 
  fetch('/api/sensitive').then(res => res.json());

// 条件执行:有权限则获取,无权限则跳过
conditionalPromise(hasPermission, fetchSensitiveData())
  .then(data => {
    if (data) {
      console.log('敏感数据:', data);
      // 渲染敏感数据
    } else {
      console.log('无权限获取敏感数据');
    }
  })
  .catch(err => console.error('获取失败:', err));

15. Promise的重试逻辑

核心作用:当Promise因暂时性错误(比如网络波动、接口临时不可用)失败时,自动重试指定次数,提升接口成功率。

// 核心封装:带重试的Promise
const retryPromise = (promiseFn, maxAttempts, interval) => {
  return new Promise((resolve, reject) => {
    // 递归重试函数
    const attempt = attemptNumber => {
      // 达到最大重试次数,触发reject
      if (attemptNumber === maxAttempts) {
        reject(new Error('Max attempts reached'));
        return;
      }
      // 执行Promise,失败则重试
      promiseFn().then(resolve).catch(() => {
        // 间隔interval毫秒后,重试下一次
        setTimeout(() => {
          attempt(attemptNumber + 1);
        }, interval);
      });
    };
    // 开始第一次尝试
    attempt(0);
  });
};

// 实战用法:接口请求失败后,重试3次,每次间隔1秒
const fetchData = () => fetch('/api/data').then(res => res.json());

retryPromise(fetchData, 3, 1000)
  .then(data => console.log('请求成功:', data))
  .catch(err => {
    console.error('重试3次仍失败:', err);
    alert('请求失败,请稍后再试!');
  });

16. Promise-pipeline(管道化异步操作)

核心作用:将多个异步操作串联成“管道”,前一个操作的结果作为后一个操作的输入,适合“多步骤异步处理”(比如数据获取→处理→保存)。

// 核心封装:Promise管道函数
const promisePipe = (...fns) => value => 
  fns.reduce((p, f) => p.then(f), Promise.resolve(value));

// 实战用法:数据处理管道(获取数据→格式化→保存)
// 步骤1:获取数据
const fetchData = () => fetch('/api/rawData').then(res => res.json());
// 步骤2:格式化数据
const formatData = (data) => {
  return new Promise(resolve => {
    const formatted = data.map(item => ({
      id: item.id,
      name: item.name,
      time: new Date(item.time).toLocaleString()
    }));
    resolve(formatted);
  });
};
// 步骤3:保存数据
const saveData = (data) => fetch('/api/save', {
  method: 'POST',
  body: JSON.stringify(data),
  headers: { 'Content-Type': 'application/json' }
}).then(res => res.json());

// 管道化执行:fetchData → formatData → saveData
const dataPipeline = promisePipe(fetchData, formatData, saveData);
dataPipeline().then(res => {
  console.log('数据处理完成:', res);
}).catch(err => {
  console.error('数据处理失败:', err);
});

17. 动态生成Promise链

核心作用:根据不同条件,动态生成异步操作链,适合“不确定异步步骤数量”的场景(比如根据用户选择,执行不同的异步操作)。

// 实战用法:根据用户选择的操作,动态生成Promise链
// 模拟用户选择的操作(可动态变化)
const userActions = [
  () => fetch('/api/action1').then(res => res.json()),
  () => fetch('/api/action2').then(res => res.json()),
  () => fetch('/api/action3').then(res => res.json())
];

// 动态生成Promise链
const promiseChain = userActions.reduce((chain, currentTask) => {
  return chain.then(currentTask); // 依次添加异步任务到链中
}, Promise.resolve()); // 初始值

// 执行动态生成的Promise链
promiseChain.then(results => {
  console.log('所有动态操作完成:', results);
}).catch(err => {
  console.error('动态操作失败:', err);
});

18. 连续获取不确定数量的数据页

核心作用:获取分页数据时,不知道总页数,通过递归方式,自动获取所有分页数据,直到没有下一页,适合“无限滚动”“批量导出”场景。

// 核心封装:递归获取所有分页数据
async function fetchPages(apiEndpoint, page = 1, allResults = []) {
  try {
    // 发起分页请求
    const response = await fetch(`${apiEndpoint}?page=${page}`);
    const data = await response.json();
    // 合并当前页数据
    const newResults = allResults.concat(data.results);
    // 判断是否有下一页,有则继续递归,无则返回所有数据
    if (data.nextPage) {
      return fetchPages(apiEndpoint, page + 1, newResults);
    } else {
      return newResults;
    }
  } catch (error) {
    console.error('获取分页数据失败:', error);
    throw error; // 抛出错误,让调用者处理
  }
}

// 实战用法:获取所有用户分页数据(不知道总页数)
fetchPages('/api/users')
  .then(allUsers => {
    console.log('所有用户数据:', allUsers);
    // 渲染所有用户数据
  })
  .catch(err => {
    alert('获取用户数据失败,请重试!');
  });

五、高级进阶用法(5种)—— 提升编码上限,面试加分项

这5种用法属于进阶技巧,在复杂项目中经常用到,掌握它们,能让你在异步开发中更游刃有余,也是前端面试的高频加分项。

19. 使用Generators管理异步流程

核心作用:将async/await与Generators配合,创建可控制的异步流程管理器,适合“复杂异步流程控制”(比如暂停、继续、中断异步操作)。

// 基础用法:Generator配合Promise管理流程
function* asyncGenerator() {
  console.log('开始执行异步流程');
  //  yield后面跟Promise,暂停等待Promise完成
  const result1 = yield fetch('/api/data1').then(res => res.json());
  console.log('第一个异步操作完成:', result1);
  
  // 第二个异步操作,依赖第一个的结果
  const result2 = yield fetch(`/api/data2?userId=${result1.id}`).then(res => res.json());
  console.log('第二个异步操作完成:', result2);
  
  return result2; // 返回最终结果
}

// 执行Generator函数
function runGenerator(generator) {
  const iterator = generator();
  const handle = (result) => {
    if (result.done) return result.value;
    // 处理yield返回的Promise
    result.value.then(res => {
      handle(iterator.next(res));
    }).catch(err => {
      iterator.throw(err);
    });
  };
  handle(iterator.next());
}

// 调用:执行异步流程
runGenerator(asyncGenerator);

20. 流式处理大型数据集

核心作用:处理大型数据集时,避免一次性加载所有数据导致内存过载,通过“流式处理”(分块处理),提升性能和用户体验。

// 实战用法:流式处理大型Excel数据(分块读取、分块处理)
async function processLargeDataSet(dataSet) {
  // dataSet:大型数据集(比如10万条数据),按块分割
  for (const dataChunk of dataSet) {
    // 分块处理数据(每个块单独异步处理)
    const processedChunk = await processData(dataChunk); // 处理单个块
    // 分块保存数据,避免一次性保存导致卡顿
    await saveProcessedData(processedChunk);
    console.log('处理完成一个数据块');
  }
  console.log('所有大型数据处理完成');
}

// 模拟分块处理函数
function processData(chunk) {
  return new Promise(resolve => {
    // 模拟数据处理(比如格式化、过滤)
    const processed = chunk.map(item => item * 2);
    setTimeout(() => resolve(processed), 500);
  });
}

// 模拟分块保存函数
function saveProcessedData(chunk) {
  return fetch('/api/saveChunk', {
    method: 'POST',
    body: JSON.stringify(chunk),
    headers: { 'Content-Type': 'application/json' }
  }).then(res => res.json());
}

// 模拟大型数据集(10个块,每个块1万条数据)
const largeDataSet = Array(10).fill(0).map(() => Array(10000).fill(0).map((_, i) => i));
// 流式处理
processLargeDataSet(largeDataSet);

21. 使用Promise实现简易的异步锁

核心作用:在多线程/多异步场景中,确保同一时间只有一个异步操作执行(比如防止重复提交、并发修改数据),避免数据冲突。

// 核心封装:简易异步锁
let lock = Promise.resolve(); // 初始锁:已解锁状态

// 获取锁
const acquireLock = () => {
  let release;
  // 等待锁释放的Promise
  const waitLock = new Promise(resolve => {
    release = resolve; // 释放锁的方法
  });
  // 尝试获取锁:当前锁释放后,返回释放锁的方法
  const tryAcquireLock = lock.then(() => release);
  // 更新锁状态:当前锁变为等待状态
  lock = waitLock;
  return tryAcquireLock;
};

// 实战用法:防止重复提交表单
async function submitForm(formData) {
  // 获取锁,获取成功才能执行提交
  const release = await acquireLock();
  try {
    console.log('开始提交表单');
    // 模拟表单提交(异步操作)
    const res = await fetch('/api/submit', {
      method: 'POST',
      body: formData
    }).then(res => res.json());
    console.log('表单提交成功:', res);
    return res;
  } catch (err) {
    console.error('表单提交失败:', err);
  } finally {
    // 无论成功失败,都释放锁
    release();
    console.log('锁已释放');
  }
}

// 模拟多次点击提交按钮(只会执行一次提交)
submitForm({ name: '张三' });
submitForm({ name: '张三' });
submitForm({ name: '张三' });

22. 组合多个Promise操作为一个函数

核心作用:将多个相关的Promise操作合并为一个函数,实现代码复用,减少冗余,提升代码可维护性。

// 实战用法:组合“获取数据+处理数据”为一个函数
// 处理数据的辅助函数
const processData = (data) => {
  return new Promise(resolve => {
    // 模拟数据处理(过滤、格式化)
    const processed = data.filter(item => item.status === 1).map(item => ({
      id: item.id,
      name: item.name,
      time: item.createTime
    }));
    resolve(processed);
  });
};

// 组合多个Promise操作
const fetchDataAndProcess = async url => {
  // 步骤1:获取数据
  const response = await fetch(url);
  const rawData = await response.json();
  // 步骤2:处理数据
  const processedData = await processData(rawData);
  // 返回处理后的结果
  return processedData;
};

// 复用函数:获取不同接口的数据并处理
fetchDataAndProcess('/api/users')
  .then(users => console.log('处理后的用户数据:', users))
  .catch(err => console.error('处理失败:', err));

fetchDataAndProcess('/api/goods')
  .then(goods => console.log('处理后的商品数据:', goods))
  .catch(err => console.error('处理失败:', err));

23. 处理可选的异步操作

核心作用:处理“可选的异步操作”,当条件满足时执行异步操作,不满足时返回默认值,避免多余的错误处理。

// 核心封装:处理可选异步操作
async function optionallyAsyncTask(condition, asyncOperation, fallbackValue) {
  if (condition) {
    // 条件满足,执行异步操作
    return await asyncOperation;
  } else {
    // 条件不满足,返回默认值
    return fallbackValue;
  }
}

// 实战用法:根据用户是否登录,决定是否获取用户个性化数据
const isLogin = () => !!localStorage.getItem('token'); // 判断用户是否登录
const fetchPersonalData = () => fetch('/api/personal').then(res => res.json()); // 个性化数据接口

// 调用:登录则获取个性化数据,未登录则返回默认数据
optionallyAsyncTask(isLogin(), fetchPersonalData(), {
  name: '游客',
  recommend: []
}).then(data => {
  console.log('用户数据:', data);
  // 渲染用户数据(无论是否登录,都有默认值,避免报错)
  document.querySelector('.user-info').textContent = JSON.stringify(data);
});

六、Promise高频避坑总结(新手必记,少踩雷)

很多新手学完Promise,一写代码就报错,不是语法错了,就是用法不对,整理了5个高频坑,记牢就能避开!

  • 坑1:忘记处理Promise的reject——Promise失败时,若未用catch捕获,会报未捕获错误,导致页面崩溃,务必给每个Promise添加catch处理。
  • 坑2:混淆Promise.all和Promise.allSettled——前者只要一个失败就整体失败,后者无论成败都会等待所有执行完成,按需选择。
  • 坑3:认为Promise可以直接取消——原生Promise无法取消,需通过封装(添加取消标记)模拟取消逻辑。
  • 坑4:await后面跟非Promise值——await只能等待Promise或async函数,若跟普通值,会直接返回该值,相当于同步执行,无意义。
  • 坑5:重复调用resolve/reject——Promise状态一旦改变就无法逆转,重复调用resolve/reject无效,还可能导致逻辑混乱,可用onceResolvedPromise避免。

七、面试高频考点(新手必记,轻松拿捏面试官)

Promise是前端面试高频考点,不用死记硬背,记住这5个核心问题,面试时直接套用即可:

  • Q1:Promise有哪三种状态?状态能否逆转? A:pending(等待中)、fulfilled(成功)、rejected(失败);状态一旦改变(pending→fulfilled或pending→rejected),就无法逆转。
  • Q2:Promise.all和Promise.race的区别? A:Promise.all等待所有Promise成功,一个失败则整体失败;Promise.race只取最快完成的Promise结果,无论成功还是失败。
  • Q3:async/await和Promise的关系? A:asyncawait是Promise的语法糖,async函数本质上返回一个Promise,await只能在async函数中使用,用于等待Promise完成。
  • Q4:如何实现Promise的取消? A:原生Promise无法直接取消,可通过添加取消标记(isCanceled),在Promise.then/catch中判断标记,实现模拟取消。
  • Q5:Promise的错误捕获有哪些方式? A:两种方式:一是给每个Promise添加.catch();二是在async/await中用try/catch捕获所有异步错误。

八、最后说几句掏心窝的话

Promise不难,核心就是“异步状态管理”,23种用法看似多,但不用死记硬背——日常开发中,高频使用的也就10种左右(async/await、Promise.all、超时处理、顺序执行等),剩下的可作为储备,用到时翻这篇文章即可。

它不是前端进阶的“加分项”,而是“必备项”——现在前端开发几乎所有异步操作都离不开Promise,不学真的会被淘汰。这篇文章整理了所有用法的实战代码、通俗解读和避坑细节,代码可直接复制练习,建议收藏起来,开发时遇到问题就翻一翻,慢慢就熟练了。

如果觉得有用,记得点赞+收藏,关注我,后续更新更多前端小白干货,一起从新手进阶成资深开发者💪