找回密码
 立即注册
首页 业界区 业界 Promise 这个新 API 真香!

Promise 这个新 API 真香!

周冰心 2025-6-2 22:32:49

  • Hey, 我是 沉浸式趣谈
  • 本文首发于【沉浸式趣谈】,我的个人博客 https://yaolifeng.com 也同步更新。
  • 转载请在文章开头注明出处和版权信息。
  • 如果本文对您有所帮助,请 点赞评论转发,支持一下,谢谢!
聊到异步,Promise 大家肯定都不陌生,是咱们处理异步操作的神器
不过呢,就算有 Promise,有时候处理一些既可能是同步又可能是异步的函数,或者那种随时可能在启动时就给你扔个同步错误的函数,还是有点小别扭。
你懂的,就是那种“我想用 .then().catch() 一把梭,但又怕它在 Promise 链开始前就崩了”的尴尬。
好消息来了!
ES2025 憋了个大招 —— Promise.try()。
Promise.try() 到底是何方神圣?

说白了,它就是 Promise 上的一个静态方法,像个万能启动器。
你扔给它一个函数(管它是同步的、异步的,会返回值还是会抛错),它都能稳稳地给你包成一个 Promise。
代码大概长这样:
  1. Promise.try(你要运行的函数, ...可能需要的参数);
复制代码
简单粗暴,对吧?
关键在于,它特别擅长处理那些“不确定性”。
比如:

  • 如果你的函数是同步的,执行完直接返回值 X?那 Promise.try() 就给你一个 resolved 状态、值为 X 的 Promise。
  • 要是函数同步执行时直接 throw new Error() 了呢?(这种最头疼了,以前可能直接崩掉后续代码)Promise.try() 会捕获这个错误,然后给你一个 rejected 状态的 Promise,错误就在里面,你可以用 .catch() 接住。简直完美!
  • 那如果函数本身就返回一个异步的 Promise 呢?没问题,Promise.try() 就直接用那个 Promise 的状态。
为啥我们需要这玩意儿?以前不也活得好好的?
嗯... 活得好是好,但可能不够优雅,或者说,不够省心。
记得以前咱们想统一处理同步/异步函数时,可能会用 Promise.resolve().then(func) 这招吗?
  1. const f = () => console.log('我应该立刻执行!');
  2. Promise.resolve().then(f); // 但它被塞到微任务队列里去了
  3. console.log('我先执行了...');
  4. // 输出:
  5. // 我先执行了...
  6. // 我应该立刻执行!
复制代码
明明 f 是个同步函数,结果被 then 这么一搞,硬生生变成了异步执行。
有时候我们并不想要这种延迟。而且,如果 f 本身在执行前就抛错,Promise.resolve() 可管不了。
Promise.try() 就是来解决这个痛点的。
它能让你的函数(如果是同步的)基本上是立即尝试执行,同时还保证了无论如何你都能拿到一个 Promise,并且同步错误也能被链式捕获。
...呃,或者更准确地说,它提供了一个统一的、更安全的 Promise 启动方式。
来,上代码感受下


  • 搞定同步函数:
  1. const syncTask = () => {
  2.     console.log('同步任务跑起来~');
  3.     return '同步搞定';
  4. };
  5. Promise.try(syncTask)
  6.     .then(res => console.log('结果:', res)) // 立刻输出 "同步搞定"
  7.     .catch(err => console.error('出错了?', err));
  8. // 控制台会先打印 "同步任务跑起来~",然后是 "结果: 同步搞定"
复制代码

  • 处理异步函数(这个没啥特别,就是正常用):
  1. const asyncTask = () => new Promise(resolve => setTimeout(() => resolve('异步也 OK'), 500));
  2. Promise.try(asyncTask)
  3.     .then(res => console.log('结果:', res)) // 大约 500ms 后输出 "异步也 OK"
  4.     .catch(err => console.error('出错了?', err));
复制代码

  • 最妙的地方:捕获同步错误
  1. const potentiallyExplodingTask = () => {
  2.     if (Math.random() < 0.5) {
  3.         // 假设这里有 50% 概率直接炸
  4.         throw new Error('Boom! 同步错误');
  5.     }
  6.     return '安全通过';
  7. };
  8. // 多试几次你就能看到效果
  9. Promise.try(potentiallyExplodingTask)
  10.     .then(res => console.log('这次运气不错:', res))
  11.     .catch(err => console.error('捕获到错误:', err.message)); // 能抓住那个 "Boom!"
复制代码
就算 potentiallyExplodingTask 在 Promise 链条“正式”开始前就同步抛错了,Promise.try() 也能稳稳接住,交给你后面的 .catch() 处理。
这在以前,可能就直接导致程序崩溃或者需要写额外的 try...catch 块了。(这点我个人觉得超级实用!)
这东西有啥好的?总结一下哈:


  • 入口统一: 不管三七二十一,同步异步函数塞进去,出来的都是 Promise,后续处理逻辑可以写得非常一致。代码看着就清爽多了。
  • 同步错误保险: 这是重点!能捕获启动函数时的同步错误,塞到 Promise 链里,让你用 .catch() 一勺烩了。避免了裸露的 try...catch 或者漏抓错误的风险。
  • 可读性提升: 意图更明显,一看 Promise.try() 就知道这里是安全启动一个可能同步也可能异步的操作。
实战中能怎么玩?


  • 调 API: fetch 本身返回 Promise,但之前的 URL 处理、参数构造啥的可能是同步的,万一出错呢?

    • 用 Promise.try(() => fetch(buildUrl(params))) 就很稳。
    1. // 以前的写法
    2. function fetchUserData(userId) {
    3.     try {
    4.         // 这里的 buildApiUrl 可能会同步抛出错误
    5.         const url = buildApiUrl(`/users/${userId}`);
    6.         return fetch(url).then(res => res.json());
    7.     } catch (err) {
    8.         return Promise.reject(err); // 手动转换成 Promise 错误
    9.     }
    10. }
    11. // 使用 Promise.try 的写法
    12. function fetchUserData(userId) {
    13.     return Promise.try(() => {
    14.         const url = buildApiUrl(`/users/${userId}`);
    15.         return fetch(url).then(res => res.json());
    16.     });
    17. }
    18. // 调用示例
    19. fetchUserData('123')
    20.     .then(data => console.log('用户数据:', data))
    21.     .catch(err => console.error('获取用户数据失败:', err));
    复制代码
  • 混合任务链: 比如先跑个同步任务,再根据结果跑个异步任务,用 Promise.try(syncTask).then(res => Promise.try(() => asyncTask(res))) 串起来就很自然。
    1. // 假设我们有个处理用户输入的场景
    2. function validateInput(input) {
    3.     // 同步验证,可能会抛出错误
    4.     if (!input || input.length < 3) {
    5.         throw new Error('输入太短了!');
    6.     }
    7.     return input.trim().toLowerCase();
    8. }
    9. function saveToDatabase(processedInput) {
    10.     // 异步保存,返回 Promise
    11.     return new Promise((resolve, reject) => {
    12.         setTimeout(() => {
    13.             if (processedInput === 'admin') {
    14.                 reject(new Error('不能使用保留关键字'));
    15.             } else {
    16.                 resolve({ success: true, id: Date.now() });
    17.             }
    18.         }, 500);
    19.     });
    20. }
    21. // 使用 Promise.try 组合这两个任务
    22. function processUserInput(rawInput) {
    23.     return Promise.try(() => validateInput(rawInput)).then(validInput =>
    24.         Promise.try(() => saveToDatabase(validInput))
    25.     );
    26. }
    27. // 测试不同情况
    28. processUserInput('') // 同步错误
    29.     .then(result => console.log('保存成功:', result))
    30.     .catch(err => console.error('处理失败:', err.message));
    31. processUserInput('admin') // 异步错误
    32.     .then(result => console.log('保存成功:', result))
    33.     .catch(err => console.error('处理失败:', err.message));
    34. processUserInput('user123') // 成功情况
    35.     .then(result => console.log('保存成功:', result))
    36.     .catch(err => console.error('处理失败:', err.message));
    复制代码
  • 数据库/文件操作: 很多库的 API 设计可能五花八门,有的同步出错有的异步出错,用 Promise.try 包裹一下,可以简化错误处理逻辑。(当然,具体库可能有自己的最佳实践,这只是个思路)
    1. // 假设我们有个文件操作库,它的 API 设计有点混乱
    2. const fileOps = {
    3.     readConfig(path) {
    4.         // 这个方法可能同步抛错(比如路径格式不对)
    5.         // 也可能返回 Promise(实际读取文件时)
    6.         if (!path.endsWith('.json')) {
    7.             throw new Error('配置文件必须是 JSON 格式');
    8.         }
    9.         return new Promise((resolve, reject) => {
    10.             setTimeout(() => {
    11.                 if (path.includes('nonexistent')) {
    12.                     reject(new Error('文件不存在'));
    13.                 } else {
    14.                     resolve({ version: '1.0', settings: { theme: 'dark' } });
    15.                 }
    16.             }, 100);
    17.         });
    18.     },
    19. };
    20. // 不使用 Promise.try 的话,调用方需要自己处理同步错误
    21. function loadAppConfig_old(configPath) {
    22.     try {
    23.         return fileOps.readConfig(configPath).then(config => {
    24.             console.log('配置加载成功');
    25.             return config;
    26.         });
    27.     } catch (err) {
    28.         console.error('同步错误:', err);
    29.         return Promise.reject(err);
    30.     }
    31. }
    32. // 使用 Promise.try,代码更简洁,错误处理更统一
    33. function loadAppConfig(configPath) {
    34.     return Promise.try(() => fileOps.readConfig(configPath)).then(config => {
    35.         console.log('配置加载成功');
    36.         return config;
    37.     });
    38. }
    39. // 测试各种情况
    40. loadAppConfig('settings.txt') // 同步错误 - 非 JSON 文件
    41.     .catch(err => console.error('加载失败:', err.message));
    42. loadAppConfig('nonexistent.json') // 异步错误 - 文件不存在
    43.     .catch(err => console.error('加载失败:', err.message));
    44. loadAppConfig('settings.json') // 成功情况
    45.     .then(config => console.log('配置内容:', config))
    46.     .catch(err => console.error('加载失败:', err.message));
    复制代码
聊了这么多,总而言之...
Promise.try() 这哥们儿,虽然看起来只是个小补充,但它解决的痛点可是实实在在的。
它让 Promise 的使用,尤其是在链条的起点上,变得更健壮、更统一。
我个人感觉,一旦大家习惯了它带来的便利(特别是那个同步错误捕获!),估计很快就会成为咱们工具箱里的常客了。
有机会的话,真得试试看!
其他好文推荐

搞定 XLSX 预览?别瞎找了,这几个库(尤其最后一个)真香!
实战分享】10 大支付平台全方面分析,独立开发必备!
关于 MCP,这几个网站你一定要知道!
做 Docx 预览,一定要做这个神库!!
【完整汇总】近 5 年 JavaScript 新特性完整总览
关于 Node,一定要学这个 10+万 Star 项目!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

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