前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >新手们容易在Promise上挖的坑~

新手们容易在Promise上挖的坑~

作者头像
用户1687375
发布2018-06-08 12:07:32
1.5K0
发布2018-06-08 12:07:32
举报
文章被收录于专栏:较真的前端

本文翻译至Nolan Lawson的一篇博客——《We have a problem with promises》

关于Promise

大家通常认为Promise是ES6提供的一个书写异步代码的解决方案,他的主要贡献是解决了“回调地狱”,但其实Promise更多的是提供了一种代码结构和流程控制机制。所以很多新手刚开始学习和使用Promise时,如果思路不能转换过来的话,经常会出现一些本末倒置的错误。

希望通过列举出下面新手的错误让大家能巩固一下关于Promise的基础知识

新手错误列举

#1 回调地狱版Promise

观察大家如何使用 PouchDB 这类大型的 promise 风格的API,我发现大量错误的 promise 使用形式。最常见的错误就是下面这个:

是的,实际上你可以像使用回调一样使用 promises,恩,就像用打磨机去削脚趾甲一样,你确实可以这么做。

其实有些老司机也会犯这样的错误。

正确的代码风格应该是下面这样的:

这种写法被称为 composing promises ,是 promises 的强大能力之一。每一个函数只会在前一个 promise 被调用并且完成回调后调用,并且这个函数会被前一个 promise 的输出调用,稍后我们在这块做更多的讨论。

#2 不知如何将Promise和forEach结合

这里是大多数人对于 promises 的理解开始出现偏差。一旦当他们要使用他们熟悉的 forEach() 循环 (无论是 for 循环还是 while 循环),他们完全不知道如何将 promises 与其一起使。因此他们就会写下类似这样的代码。

这里的问题在于第一个then之中的并没有返回值,导致这个then会立即决议为undefined并执行第二个then中的操作。

这是一个非常隐蔽的 bug,因为如果 PouchDB 删除这些文档足够快,你的 UI 界面上显示的会完成正常,你可能会完全注意不到有什么东西有错误。这个 bug 可能会在一些古怪的竞态问题或一些特定的浏览器中暴露出来,并且到时可能几乎没有可能去定位问题。

简而言之,forEach()/for/while 并非你寻找的解决方案。你需要的是 Promise.all():

上面的代码是什么意思呢?大体来说,Promise.all()会以一个 promises 数组为输入,并且返回一个新的 promise。这个新的 promise 会在数组中所有的 promises 都成功返回后才返回。他是异步版的 for 循环。

并且 Promise.all() 会将执行结果组成的数组返回到下一个函数,比如当你希望从 PouchDB 中获取多个对象时,会非常有用。此外一个更加有用的特效是,一旦数组中的 promise 任意一个返回错误,Promise.all() 也会返回错误。

#3 忘记使用.catch()

这是另一个常见的错误。单纯的坚信自己的 promises 会永远不出现异常,很多开发者会忘记在他们的代码中添加一个 .catch()。然而不幸的是这也意味着,任何被抛出的异常都会被吃掉,并且你无法在 console 中观察到他们。这类问题 debug 起来会非常痛苦。

为了避免这类讨厌的场景,我习惯于像下面的代码一样使用 promise:

即使你坚信不会出现异常,添加一个 catch() 总归是更加谨慎的。如果你的假设最终被发现是错误的,它会让你的生活更加美好。

#4 使用“deferred”

简单的说,promises 拥有一个漫长并且戏剧化的历史,Javascript 社区花费了大量的时间让其走上正轨。在早期,deferred 在 Q,When,RSVP,Bluebird,Lie等等的 “优秀” 类库中被引入, jQuery 与 Angular 在使用 ES6 Promise 规范之前,都是使用这种模式编写代码。

因此如果你在你的代码中使用了这个词 (我不会把这个词重复第三遍!),你就做错了。下面是说明一下如何避免它。

首先,大部分 promises 类库都会提供一个方式去包装一个第三方的 promises 对象。举例来说,Angular的 $q 模块允许你使用 $q.when包裹非 $q 的 promises。因此 Angular 用户可以这样使用 PouchDB promises.

另一种策略是使用构造函数声明模式,它在用来包裹非 promise API 时非常有用。举例来说,为了包裹一个回调风格的 API 如 Node 的 fs.readFile ,你可以简单的这么做:

#5 使用副作用调用而非返回

下面的代码有什么问题?

好了,现在是时候讨论一下关于 promises 你所需要知道的一切。

认真的说,这是一个一旦你理解了它,就会避免所有我提及的错误的古怪的技巧。你准备好了么?

就如我前面所说,promises 的奇妙在于给予我们以前的 return 与 throw。但是在实践中这到底是怎么一回事呢?

每一个 promise 都会提供给你一个 then() 函数 (或是 catch(),实际上只是 then(null, ...) 的语法糖)。当我们在 then() 函数内部时:

我们可以做什么呢?有三种事情:

  1. return 另一个 promise
  2. return一个同步的值(或undefined)
  3. throw一个同步异常

就是这样。一旦你理解了这个技巧,你就理解了 promises。

关于Promise最后的话

Promises 是非常赞的。如果你还在使用回调模式,我强烈建议你切换到 promises。你的代码会变的更少,更优雅,并且更加容易理解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-10-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 较真的前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档