查看: 231|回复: 0

Node.js Promise链式调用与错误处理实践

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在 Node.js 异步编程中,Promise 用来表达异步操作的最终结果,并通过 then/catch 组织成功与失败分支,避免回调嵌套过深。原文从状态、创建、链式调用、静态方法、错误处理到 async/await 给出了一套入门示例。下面按脚本开发中实际会遇到的代码组织方式来重组这些知识点,代码逻辑和输出结论保持与原文一致。

一、状态与创建方式
Promise 有三种状态:Pending(等待中)、Fulfilled(已成功)、Rejected(已失败)。状态一旦从 Pending 变为 Fulfilled 或 Rejected,就不会再改变。创建 Promise 时传入执行器,由 resolve 和 reject 改变状态:
  1. const myPromise = new Promise((resolve, reject) => {
  2.   // 异步操作
  3.   const success = true; // 假设这是异步操作的结果
  4.   if (success) {
  5.     resolve('操作成功!'); // 状态变为 fulfilled
  6.   } else {
  7.     reject('操作失败!'); // 状态变为 rejected
  8.   }
  9. });
复制代码
这段示例中的 success 只是模拟异步结果;真实脚本里通常把文件读写、网络请求等异步结果交给 resolve 或 reject。

二、then、catch 与 finally
使用 .then() 处理成功结果,.catch() 处理失败,.finally() 在成功或失败后都会执行,适合做资源清理:
  1. myPromise
  2.   .then((result) => {
  3.     console.log(result); // 输出:操作成功!
  4.   })
  5.   .catch((error) => {
  6.     console.error(error); // 输出:操作失败!
  7.   })
  8.   .finally(() => {
  9.     console.log('操作完成(无论成功或失败)');
  10.   });
复制代码
这里 finally 的回调不改变 Promise 的结果,原文把它定位为最终清理操作。

三、链式调用组织多个异步步骤
当多个异步操作有先后依赖时,可以让 then 返回下一个 Promise,使多个步骤串成链:
  1. function asyncOperation1() {
  2.   return new Promise((resolve) => {
  3.     setTimeout(() => resolve('第一步完成'), 1000);
  4.   });
  5. }
  6. function asyncOperation2(data) {
  7.   return new Promise((resolve) => {
  8.     setTimeout(() => resolve(`${data}, 第二步完成`), 1000);
  9.   });
  10. }
  11. asyncOperation1()
  12.   .then((result) => asyncOperation2(result))
  13.   .then((finalResult) => {
  14.     console.log(finalResult); // 输出:第一步完成, 第二步完成
  15.   })
  16.   .catch((error) => {
  17.     console.error('链式中出错:', error);
  18.   });
复制代码
这种写法把嵌套回调改成横向链路。关键点是 then 中返回 Promise,后续 then 才能拿到下一步结果。

四、并发组合静态方法
Promise.all()、Promise.allSettled()、Promise.race()、Promise.any() 用来处理多个 Promise 的组合结果,触发失败的条件不同。
Promise.all() 等待全部成功,任意一个失败就进入失败分支:
  1. const promise1 = Promise.resolve('第一个');
  2. const promise2 = Promise.resolve('第二个');
  3. Promise.all([promise1, promise2])
  4.   .then((results) => {
  5.     console.log(results); // 输出:['第一个', '第二个']
  6.   })
  7.   .catch((error) => {
  8.     console.error('其中一个失败:', error);
  9.   });
复制代码
Promise.allSettled() 等待所有 Promise 完成,不管成功还是失败,然后逐个检查 status:
  1. const promise1 = Promise.resolve('成功');
  2. const promise2 = Promise.reject('失败');
  3. Promise.allSettled([promise1, promise2])
  4.   .then((results) => {
  5.     results.forEach(result => {
  6.       if (result.status === 'fulfilled') {
  7.         console.log('成功:', result.value);
  8.       } else {
  9.         console.log('失败:', result.reason);
  10.       }
  11.     });
  12.   });
复制代码
Promise.race() 返回最先完成的 Promise;示例中第二个定时器更短,因此输出第二个:
  1. const promise1 = new Promise((resolve) => setTimeout(resolve, 500, '第一个'));
  2. const promise2 = new Promise((resolve) => setTimeout(resolve, 100, '第二个'));
  3. Promise.race([promise1, promise2])
  4.   .then((result) => {
  5.     console.log(result); // 输出:第二个
  6.   });
复制代码
Promise.any() 返回第一个成功的 Promise,失败项会被忽略;只有全部失败时才进入 catch:
  1. const promise1 = Promise.reject('失败1');
  2. const promise2 = Promise.resolve('成功2');
  3. Promise.any([promise1, promise2])
  4.   .then((result) => {
  5.     console.log(result); // 输出:成功2
  6.   })
  7.   .catch((error) => {
  8.     console.log('所有都失败了:', error);
  9.   });
复制代码
从原文对比可归纳:all 是任意失败即失败;allSettled 永不因单个失败而失败;race 由最先完成者决定,最先失败则失败;any 只在全部失败时失败。选用哪个方法,取决于脚本要“全部成功才继续”“收集全部结果”还是“取最快结果/取首个成功结果”。

五、错误处理写法与链式 catch
Promise 错误处理有两种常见写法:使用 .catch(),或使用 .then() 的第二个参数:
  1. someAsyncFunction()
  2.   .then((result) => { /* 处理成功 */ })
  3.   .catch((error) => { /* 处理所有错误 */ });
  4. someAsyncFunction()
  5.   .then(
  6.     (result) => { /* 处理成功 */ },
  7.     (error) => { /* 处理失败 */ }
  8.   );
复制代码
原文强调,如果中间 then 中抛出错误,后面的 catch 会捕获:
  1. someAsyncFunction()
  2.   .then((result) => {
  3.     throw new Error('中间处理出错');
  4.   })
  5.   .catch((error) => {
  6.     console.error('捕获到:', error);
  7.   });
复制代码
因此链式调用时,推荐在所有 then 之后只放一个 catch,把流程中的错误集中处理:
  1. fetchData()
  2.   .then(processData)
  3.   .then(saveData)
  4.   .then(displayResult)
  5.   .catch(error => console.error('流程中出错:', error));
复制代码
这段代码中的四个函数在原示例中作为流程节点示意,实际项目里应保证它们返回可继续链式处理的值或 Promise。

六、async/await 改写
ES2017 引入 async/await,让 Promise 的使用更接近同步代码。原文示例用 try/catch 包住 await 调用:
  1. async function runOperations() {
  2.   try {
  3.     const result1 = await asyncOperation1();
  4.     const result2 = await asyncOperation2(result1);
  5.     console.log(result2);
  6.   } catch (error) {
  7.     console.error(error);
  8.   }
  9. }
  10. runOperations();
复制代码
这里的两个 await 表示按顺序等待。原文提示 Promise 是现代 Node.js 异步编程的基础,理解 Promise 是掌握 async/await 的前提;实际编写时可优先用 async/await 让异步逻辑更清晰、更易维护。

七、把回调函数转成 Promise
Node.js 老式 API 常采用回调风格。原文先手写 Promise 包装 fs.readFile:
  1. const fs = require('fs');
  2. function readFilePromise(filePath) {
  3.   return new Promise((resolve, reject) => {
  4.     fs.readFile(filePath, 'utf8', (err, data) => {
  5.       if (err) {
  6.         reject(err);
  7.       } else {
  8.         resolve(data);
  9.       }
  10.     });
  11.   });
  12. }
  13. readFilePromise('example.txt')
  14.   .then(data => console.log(data))
  15.   .catch(err => console.error(err));
复制代码
随后可以用 Node.js 内置的 util.promisify 简化转换:
  1. const fs = require('fs');
  2. const util = require('util');
  3. const readFilePromise = util.promisify(fs.readFile);
  4. const writeFilePromise = util.promisify(fs.writeFile);
  5. readFilePromise('example.txt', 'utf8')
  6.   .then(data => {
  7.     console.log(data);
  8.     return writeFilePromise('output.txt', data);
  9.   })
  10.   .then(() => console.log('写入完成'))
  11.   .catch(err => console.error(err));
复制代码
这段流程先读 example.txt,再把读取内容写入 output.txt;每一步返回 Promise,错误统一落到 catch。

八、开发中的实践清单
结合原文最佳实践和示例,可整理为:总是处理错误,不忽略 catch 或 try-catch;避免 Promise 嵌套,用链式调用或 async/await 保持代码扁平;给 Promise 变量起有意义的名称;在函数中返回 Promise,方便调用方继续链式处理;用 finally 做清理,无论成功或失败都执行。状态、创建、then/catch、链式调用、静态方法、async/await 和 util.promisify 构成了原文的入门主线。对 Node.js 脚本而言,先明确异步步骤是串行还是并发,再选择 then 链、async/await 或 Promise.all 等组合方法,能减少回调嵌套并让错误出口更集中。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-26 14:49 , Processed in 0.024212 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部