• Hey, 我是 沉浸式趣谈
  • 本文首发于【沉浸式趣谈】,我的个人博客 https://yaolifeng.com 也同步更新。
  • 转载请在文章开头注明出处和版权信息。
  • 如果本文对您有所帮助,请 点赞评论转发,支持一下,谢谢!

聊到异步,Promise 大家肯定都不陌生,是咱们处理异步操作的神器

不过呢,就算有 Promise,有时候处理一些既可能是同步又可能是异步的函数,或者那种随时可能在启动时就给你扔个同步错误的函数,还是有点小别扭。

你懂的,就是那种“我想用 .then().catch() 一把梭,但又怕它在 Promise 链开始前就崩了”的尴尬。

好消息来了!

ES2025 憋了个大招 —— Promise.try()

Promise.try() 到底是何方神圣?

说白了,它就是 Promise 上的一个静态方法,像个万能启动器。

你扔给它一个函数(管它是同步的、异步的,会返回值还是会抛错),它都能稳稳地给你包成一个 Promise。

代码大概长这样:

Promise.try(你要运行的函数, ...可能需要的参数);

简单粗暴,对吧?

关键在于,它特别擅长处理那些“不确定性”。

比如:

  • 如果你的函数是同步的,执行完直接返回值 X?那 Promise.try() 就给你一个 resolved 状态、值为 X 的 Promise。
  • 要是函数同步执行时直接 throw new Error() 了呢?(这种最头疼了,以前可能直接崩掉后续代码)Promise.try() 会捕获这个错误,然后给你一个 rejected 状态的 Promise,错误就在里面,你可以用 .catch() 接住。简直完美!
  • 那如果函数本身就返回一个异步的 Promise 呢?没问题,Promise.try() 就直接用那个 Promise 的状态。

为啥我们需要这玩意儿?以前不也活得好好的?

嗯... 活得好是好,但可能不够优雅,或者说,不够省心。

记得以前咱们想统一处理同步/异步函数时,可能会用 Promise.resolve().then(func) 这招吗?

const f = () => console.log('我应该立刻执行!');
Promise.resolve().then(f); // 但它被塞到微任务队列里去了
console.log('我先执行了...');
// 输出:
// 我先执行了...
// 我应该立刻执行!

明明 f 是个同步函数,结果被 then 这么一搞,硬生生变成了异步执行。

有时候我们并不想要这种延迟。而且,如果 f 本身在执行前就抛错,Promise.resolve() 可管不了。

Promise.try() 就是来解决这个痛点的。

它能让你的函数(如果是同步的)基本上是立即尝试执行,同时还保证了无论如何你都能拿到一个 Promise,并且同步错误也能被链式捕获。

...呃,或者更准确地说,它提供了一个统一的、更安全的 Promise 启动方式。

来,上代码感受下

  • 搞定同步函数:
const syncTask = () => {
console.log('同步任务跑起来~');
return '同步搞定';
}; Promise.try(syncTask)
.then(res => console.log('结果:', res)) // 立刻输出 "同步搞定"
.catch(err => console.error('出错了?', err));
// 控制台会先打印 "同步任务跑起来~",然后是 "结果: 同步搞定"
  • 处理异步函数(这个没啥特别,就是正常用):
const asyncTask = () => new Promise(resolve => setTimeout(() => resolve('异步也 OK'), 500));

Promise.try(asyncTask)
.then(res => console.log('结果:', res)) // 大约 500ms 后输出 "异步也 OK"
.catch(err => console.error('出错了?', err));
  • 最妙的地方:捕获同步错误
const potentiallyExplodingTask = () => {
if (Math.random() < 0.5) {
// 假设这里有 50% 概率直接炸
throw new Error('Boom! 同步错误');
}
return '安全通过';
}; // 多试几次你就能看到效果
Promise.try(potentiallyExplodingTask)
.then(res => console.log('这次运气不错:', res))
.catch(err => console.error('捕获到错误:', err.message)); // 能抓住那个 "Boom!"

就算 potentiallyExplodingTaskPromise 链条“正式”开始前就同步抛错了,Promise.try() 也能稳稳接住,交给你后面的 .catch() 处理。

这在以前,可能就直接导致程序崩溃或者需要写额外的 try...catch 块了。(这点我个人觉得超级实用!)

这东西有啥好的?总结一下哈:

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

实战中能怎么玩?

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

    • Promise.try(() => fetch(buildUrl(params))) 就很稳。
    // 以前的写法
    function fetchUserData(userId) {
    try {
    // 这里的 buildApiUrl 可能会同步抛出错误
    const url = buildApiUrl(`/users/${userId}`);
    return fetch(url).then(res => res.json());
    } catch (err) {
    return Promise.reject(err); // 手动转换成 Promise 错误
    }
    } // 使用 Promise.try 的写法
    function fetchUserData(userId) {
    return Promise.try(() => {
    const url = buildApiUrl(`/users/${userId}`);
    return fetch(url).then(res => res.json());
    });
    } // 调用示例
    fetchUserData('123')
    .then(data => console.log('用户数据:', data))
    .catch(err => console.error('获取用户数据失败:', err));
  • 混合任务链: 比如先跑个同步任务,再根据结果跑个异步任务,用 Promise.try(syncTask).then(res => Promise.try(() => asyncTask(res))) 串起来就很自然。

    // 假设我们有个处理用户输入的场景
    function validateInput(input) {
    // 同步验证,可能会抛出错误
    if (!input || input.length < 3) {
    throw new Error('输入太短了!');
    }
    return input.trim().toLowerCase();
    } function saveToDatabase(processedInput) {
    // 异步保存,返回 Promise
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    if (processedInput === 'admin') {
    reject(new Error('不能使用保留关键字'));
    } else {
    resolve({ success: true, id: Date.now() });
    }
    }, 500);
    });
    } // 使用 Promise.try 组合这两个任务
    function processUserInput(rawInput) {
    return Promise.try(() => validateInput(rawInput)).then(validInput =>
    Promise.try(() => saveToDatabase(validInput))
    );
    } // 测试不同情况
    processUserInput('') // 同步错误
    .then(result => console.log('保存成功:', result))
    .catch(err => console.error('处理失败:', err.message)); processUserInput('admin') // 异步错误
    .then(result => console.log('保存成功:', result))
    .catch(err => console.error('处理失败:', err.message)); processUserInput('user123') // 成功情况
    .then(result => console.log('保存成功:', result))
    .catch(err => console.error('处理失败:', err.message));
  • 数据库/文件操作: 很多库的 API 设计可能五花八门,有的同步出错有的异步出错,用 Promise.try 包裹一下,可以简化错误处理逻辑。(当然,具体库可能有自己的最佳实践,这只是个思路)

    // 假设我们有个文件操作库,它的 API 设计有点混乱
    const fileOps = {
    readConfig(path) {
    // 这个方法可能同步抛错(比如路径格式不对)
    // 也可能返回 Promise(实际读取文件时)
    if (!path.endsWith('.json')) {
    throw new Error('配置文件必须是 JSON 格式');
    } return new Promise((resolve, reject) => {
    setTimeout(() => {
    if (path.includes('nonexistent')) {
    reject(new Error('文件不存在'));
    } else {
    resolve({ version: '1.0', settings: { theme: 'dark' } });
    }
    }, 100);
    });
    },
    }; // 不使用 Promise.try 的话,调用方需要自己处理同步错误
    function loadAppConfig_old(configPath) {
    try {
    return fileOps.readConfig(configPath).then(config => {
    console.log('配置加载成功');
    return config;
    });
    } catch (err) {
    console.error('同步错误:', err);
    return Promise.reject(err);
    }
    } // 使用 Promise.try,代码更简洁,错误处理更统一
    function loadAppConfig(configPath) {
    return Promise.try(() => fileOps.readConfig(configPath)).then(config => {
    console.log('配置加载成功');
    return config;
    });
    } // 测试各种情况
    loadAppConfig('settings.txt') // 同步错误 - 非 JSON 文件
    .catch(err => console.error('加载失败:', err.message)); loadAppConfig('nonexistent.json') // 异步错误 - 文件不存在
    .catch(err => console.error('加载失败:', err.message)); loadAppConfig('settings.json') // 成功情况
    .then(config => console.log('配置内容:', config))
    .catch(err => console.error('加载失败:', err.message));

聊了这么多,总而言之...

Promise.try() 这哥们儿,虽然看起来只是个小补充,但它解决的痛点可是实实在在的。

它让 Promise 的使用,尤其是在链条的起点上,变得更健壮、更统一。

我个人感觉,一旦大家习惯了它带来的便利(特别是那个同步错误捕获!),估计很快就会成为咱们工具箱里的常客了。

有机会的话,真得试试看!

其他好文推荐

搞定 XLSX 预览?别瞎找了,这几个库(尤其最后一个)真香!

实战分享】10 大支付平台全方面分析,独立开发必备!

关于 MCP,这几个网站你一定要知道!

做 Docx 预览,一定要做这个神库!!

【完整汇总】近 5 年 JavaScript 新特性完整总览

关于 Node,一定要学这个 10+万 Star 项目!

Promise 这个新 API 真香!的更多相关文章

  1. ES6的新API如Promise,Proxy,Array.form(),Object.assign()等,Babel不能转码, 使用babel-polyfill来解决

    Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator.Generator.Set.Maps.Proxy.Reflect.Symbol.Promis ...

  2. 一文搞懂 Promise 新 Api allSettled 的用法和 all 区别,以及如何在不支持新特性的环境下实现一个 Polyfill

    开始 一文搞懂 Promise 新 Api allSettled 的用法和 all 区别,以及如何在不支持新特性的环境下实现一个 Polyfill allSettled 的用法 const runAl ...

  3. 你只会用 StringBuilder?试试 StringJoiner,真香!

    你只会用 StringBuilder/ StringBuffer 拼接字符串? 那你就 OUT 了!! 如果需要拼接分隔符的字符串,建议使用 Java 8 中的这款拼接神器:StringJoiner, ...

  4. 从Eclipse切换到IDEA工具,哎~真香!

    从Eclipse切换到IDEA工具,哎~真香!(图) 个人观点:IDEA工具用了就回不去了!!!对比很多人写,我就不赘述了.我在这里主要介绍一下IDEA工具的一些使用上的技巧,毕竟我开始学习java的 ...

  5. VSCode六大通用插件真香合集

    目录 一.background:设置心水背景图 安利理由: 安装及设置步骤: 设置过程中使用的代码: 成果展示: 注意: 二.Material Theme(VSCode主题)+Material Ico ...

  6. JavaFX桌面应用-MVC模式开发,“真香”

    使用mvc模块开发JavaFX桌面应用在JavaFX系列文章第一篇 JavaFX桌面应用开发-HelloWorld 已经提到过,这里单独整理使用mvc模式开发开发的流程. ~ JavaFX桌面应用开发 ...

  7. 国人开源了一款超好用的 Redis 客户端,真香!!

    大家都知道,Redis Desktop Manager 是一款非常好用的 Redis 可视化客户端工具,但可惜的是 v0.9.4 版本之后需要收费了: 这个工具不再免费提供安装包了,要对所有安装包收费 ...

  8. 我把公司 10 年老系统改造 Maven,真香!!

    公司有几个老古董项目,应该是 10 年前开发的了,有一个是 JSP + Servlet,有一个还用的 SSH 框架,打包用的 Ant,是有多老啊,我想在座的各位很多都没听过吧. 为了持续集成.持续部署 ...

  9. 我在苦苦坚持的时候,WebStorm已经悄悄的“真香”起来

    前言 最近接了一个活儿,是用WebStorm开发一个基于VUE的网站,但是我真的是几乎没接触过VUE相关的项目实践,更别说用WebStorm在实际中的应用,之前只是听朋友说多好用,但是,因为现有工具不 ...

  10. 联合迭代器与生成器,enumerate() 内置函数真香!

    花下猫语:Python 中很多内置函数的作用都非常大,比如说 enumerate() 和 zip(),它们使得我们在作迭代操作时极为顺手.这是一篇很多年前的 PEP,提议在 Python 2.3 版本 ...

随机推荐

  1. Linux命令格式详解

    Linux命令格式详解 在Linux系统中,命令行界面是用户与系统交互的重要方式之一.通过命令行,用户可以执行各种任务,从简单的文件操作到复杂的系统配置.为了更有效地使用命令行,理解Linux命令的基 ...

  2. CAD内核的奥秘 | 工业软件发展史 (转)

    CAD内核的奥秘 | 工业软件发展史 (声明:此文非本人原著,仅供交流,如果侵犯到原作者权利,立即删除) 如果一个产业要寻根,就会发现一个万千世界,最后会聚焦到一个点上. "一沙一世界&qu ...

  3. mybatis mysql count(*) 返回结果为null的解决

    具体错误信息: org.apache.ibatis.binding.BindingException: Mapper method 'com.xx.xx.xx.xx.xx.getCount attem ...

  4. autMan奥特曼机器人-Linux、Windows、docker安装教程

    autMan简介 autMan是机器人牵引的扩展性极强的一站式解决方案 原生支持对接qq框架.qq频道.微信框架(酷V西瓜可爱猫千寻鲲鹏). 内置微信.微信客服.公众号.钉钉.飞书.tg客户端.tg机 ...

  5. Ansible - [11] Roles

    前言 Q1:什么是Roles 在实际生产环境中,会编写大量的playbook文件来实现不同的功能.而且,每个playbook还可能会调用其他文件(变量文件),对于海量的.无规律的文件,管理是个问题.A ...

  6. DW001 - 数据仓库理论知识

    数据仓库概念 数据仓库基本架构 数据集市概念 数据湖概念 数据仓库概念 数据仓库(Data Warehouse,DW)是一个面向主题的.集成的.非易失的.反映历史变化的.用来支持企业管理决策的数据集合 ...

  7. 公众号已上线 Ask AI 功能

    Get新技能,给公众号接入AI智能体,没花一分钱. 不禁感慨这时代的进步也太快了,曾经科幻小说中描绘的未来已成现实! 下面是笔者在腾讯元宝中创建的智能体"鲸鱼小助手": 如果今后要 ...

  8. 2D量测流程

  9. Keepalived学习,双主热备高可用

    双主热备可以看做双机主备的升级(双机主备链接 https://www.cnblogs.com/hmxs/p/12041735.html),它是为了让两台设备都能提供服务,而不是主节点正常时,备用节点一 ...

  10. rust学习笔记(6)

    模块 定义自己的模块,方便外部的调用 mod可以嵌套 可见程度 分为private和public 其中pub可以分为模块内可见和模块外可见 mod也遵循可见性的要求 // 一个名为 `my_mod` ...