当前位置: 首页 > 后端技术 > Node.js

什么是Promise.try以及为什么它很重要?

时间:2023-04-03 17:46:20 Node.js

本文为翻译,原文链接如下:什么是Promise.try,为什么重要?简介一个经常让Node.js社区的用户感到困惑的话题是Bluebird提供的Promise.try方法。总是很难理解它是什么,或者为什么要使用它,而且几乎所有的Promises指南都没有很好地解释它的用途。在这篇简短的文章中,我希望更好地解释什么是Promise.try以及为什么要经常使用它。我假设您已经对Promises有所了解,特别是.then方法的作用。即使您使用Promises的其他实现(例如ES6Promises),本文仍然对您有用。因为在本文的最后,我将解释如何在没有Bluebird的情况下实现相同的功能。Promise.try是什么简而言之,Promise.try类似于promiseObject.then,但没有之前的promise对象。现在这仍然有点模糊,所以让我们从一个例子开始。一些使用Promises的典型代码可能如下所示:好的。我们假设database.users.get将返回某种类型的Promise,并且该Promise最终将解析为具有name属性的对象。下面是使用Promise.try函数的相同代码:varPromise=require("bluebird");functiongetUsername(userId){returnPromise.try(function(){returndatabase.users.get({id:userID});}).then(function(user){returnuser.name;});}如您所见,我们在调用链的开头添加了Promise.try。我们不是直接在database.users.get之后链接,而是在Promise.try之后链接调用并简单地返回database.users.get的结果,就像我们通常使用.then所做的那样。Promise.try有什么用上面的代码看起来像是不必要的额外代码。但在实践中,它有几个优点:更好的错误处理:无论同步错误发生在何处,它们都作为拒绝传递;更好的互操作性:无论第三方方法使用什么Promises实现,您将始终使用您的首选实现;易于导航:所有代码都在水平方向上处于同一缩进级别,因此更容易一目了然地了解正在发生的事情。下面,我将逐一讨论这些要点。1.更好的错误处理Promises的一个受欢迎的优点是我们可以用相同的方式处理同步和异步错误——通过简单地捕获同步错误并将其与拒绝的Promise一起传递。但事实真的如此吗?让我们看一下第一个例子的一个稍微修改过的版本:修改后的版本,我们输错了user.name,现在是uesr.name。这通常会失败,因为uesr是未定义的,因此不能有任何属性。事实上,它会像我们想的那样,在.then方法中被捕获,成为一个被拒绝的Promise。但是如果database.users.get同步抛出怎么办?如果3rd方数据库代码中有错字或错误怎么办?Promises的错误捕获特性是由于它所有的同步代码都在.then方法中,所以它可以被包裹在一个巨大的try/catch块中。(译者注:promiseObj.then方法的回调函数是在try/catch块中调用的,所以可以捕获运行时的错误)。但是...我们的database.users.get不在.then中!因此,Promises的实现机制无法访问到这块代码,也无法对其进行包装。我们的同步错误仍将是同步错误,现在我们又回到了“原始时代”——不得不分别处理同步错误和异步错误。现在,让我们回到使用Promise.try的示例:);}).then(function(user){returnuser.name;});}之前我说过Promise.try就像一个.then,但没有前面的Promise。这也适用于此——它将捕获database.users.get中的同步错误,就像.then!(译者注:try和then方法的回调中的同步错误会被捕获并传递)通过使用Promise.try,我们简化了我们的错误处理以涵盖所有同步错误,而不仅仅是第一次异步操作之后的错误处理(如.然后回调)。Promises/A+在进入下一点之前,让我们先了解一下什么是Promises/A+以及它在生态系统中扮演的角色。Promises/A+官方网站总结如下:一个可靠的、可互操作的、开放的JavaScriptPromises标准,由实施者为实施者服务。换句话说,这是一种确保Promises的不同实现(Bluebird、ES6、Q、RSVP等)完美协同工作的方法。这个Promises/A+规范就是为什么你可以使用任何你喜欢的Promises实现,以及为什么你不必关心第三方库(如Knex)使用哪个Promises实现。为了说明Promises/A+为用户做了什么:所有以红色突出显示的函数返回BluebirdPromises,蓝色的返回ES6Promises,绿色的返回QPromises。请注意,即使我们在第一个.then的回调中返回了ES6Promise,第一个.then仍将返回一个BluebirdPromise。同样,即使第二个.then的回调返回QPromise,我们的doStuff方法仍会返回BluebirdPromise。发生这种情况是因为,除了捕获同步错误之外,.then还包装返回值并确保它们最终作为与调用.then方法的承诺对象相同的实现形式的承诺。实际上,这意味着链中的第一个承诺决定了您稍后将使用哪个实现。这是确保您始终使用可预测API的实用方法。您唯一需要关心的Promise实现形式是链中第一个函数使用的形式——不管后面发生什么。2.更好的互操作性然而,上述规范并不总是令人满意-例如,看看这个例子:varPromise=require("bluebird");functiongetAllUsernames(){//?这将返回一个ES6Promise。returndatabase.users.getAll().map(function(user){returnuser.name;});}如果您不熟悉地图并想了解更多信息,可以阅读这篇文章。我们在这个特定示例中使用的.map函数是一个Bluebird函数,在ES6Promises上不可用。我们不能在这里使用它,即使我们在项目中使用Bluebird-这是因为链中的第一个函数(database.users.getAll)返回ES6Promise而不是BluebirdPromise。现在,让我们再次查看同一个示例,但这次使用Promise.try:varPromise=require("bluebird");functiongetAllUsernames(){//?这将返回一个BluebirdPromise。returnPromise.try(function(){//?这将返回一个ES6Promise。returndatabase.users.getAll();}).map(function(user){returnuser.name;});}现在我们可以使用.map!由于我们从Bluebird的Promise.try开始,我们所有的Promise也将是BluebirdPromises,无论.then回调中发生什么。通过像这样使用Promise.try,您可以通过确保链中的第一个Promise来自您的首选实现来自行决定使用哪个实现。你不能用.then做到这一点,因为链头并不总是你想要的实现。3.易于浏览最后一个优势是可读性。通过使用Promise.try启动每个链,所有实际的“业务逻辑”都位于同一(水平)缩进级别的回调中。虽然这似乎是一个很小的优势,但对于浏览大量文本的人来说,它实际上会产生很大的不同。为了说明差异,下面是如何在不使用Promise.try和使用常见缩进样式的情况下直观地浏览代码:下面是使用Promise.try的相同代码:虽然代码看起来有点“嘈杂”,但它仍然可以更容易快速看懂代码,只是因为你的眼睛能“发现”的少。如何实现Promise.try对于那些不支持Promise.try的Promise实现,只要其Promise构造函数能够捕获同步错误,就很容易实现Promise.try功能。ES6Promise的构造函数可以捕获同步错误,所以我们可以这样实现Promise.try:'usestrict';exportfunctionpromiseTry(func){returnnewPromise(function(resolve,reject){resolve(func());});}目前我的polyfill也添加了Promise.try方法:https://github.com/lyl123321/……