Mongoose: 能够指定 ES6 承诺库 mongoose 使用哪个

创建于 2015-02-17  ·  45评论  ·  资料来源: Automattic/mongoose

请参阅关于 #1699 的讨论

最有用的评论

是的require('mongoose').Promise = global.Promise将使猫鼬使用本机承诺。 你应该可以使用任何 ES6 Promise 构造函数,但现在我们只使用原生、bluebird 和 Q进行测试

所有45条评论

真的很期待能够在所有数据库工作完成后使用 Promise.all() 来做一些事情。

:+1:

https://github.com/LearnBoost/mongoose/issues/1699上不太清楚的一件事是默认实现是什么。

好吧,鉴于它不是一个向后突破的版本, mpromise 必须是默认值,但您将能够覆盖。

+1

:+1:

是否有一个分支被黑客入侵? 我无法忍受 mpromise 的错误处理流程,我即将破解源代码以查看这将需要什么。

承诺:

  query.exec()
    .then(function(ou) {
      if(!ou) {
        return next(new errors.http.NotFound('The specified OU was either not found, or your credentials lack the required permissions to view it.'));
      }

      res.send(ou);
    }, next)
    .end(next);

必须处理拒绝并将end放在那里。 如果没有end ,异常(例如,我的 NotFound 类拼写错误)会被默默地吞并并表达只是停滞不前。

蓝鸟:

  query.exec()
    .then(function(ou) {
      if(!ou) {
        return next(new errors.http.NotFound('The specified OU was either not found, or your credentials lack the required permissions to view it.'));
      }

      res.send(ou);
    })
    .catch(next);

到目前为止, promisifyAll(require('mongoose'))似乎实际上仍在使用 Mongoose 4。 涵盖这一点的回归测试是否会过于有针对性?

现在不行。 大部分工作将按照 #2754 和 vkarpov15/kareem#2 在kareem模块中进行,因为这将允许我们用一块石头杀死两只鸟,并删除为制作钩子和承诺而编写的非常混乱的切点废话一起工作。 不过,请随意尝试一下,我对 PR 持开放态度。

但是我们为什么要继续支持其他的 Promise 库呢? ES6 Promises 规范现在坚如磐石,并且会一直存在。 我们不能只使用纯 ES6 Promises,当它们在旧版本的节点中不可用时加载一个 polyfill?

如果是这样,我可以尝试一下。

关键是你可以使用任何你喜欢的 ES6 兼容的 Promise 库。 很多人仍然在 bluebird 上投入巨资,当、q、rsvp 等等等,并且这些库中的每一个都有它自己的特殊怪癖,这是通用 polyfill 无法捕捉到的。

我对其他建议持开放态度 - 我不特别喜欢或使用承诺,这个功能的动机是这样一个事实,即人们要求“在 mpromise 中支持 bluebird 功能 X”或“原生 rsvp.js 支持”。 “让人们将自己的 promises 库带到聚会上是解决这些问题的最简单方法。

我明白你的意思了。 我是 Promises 的忠实用户和支持者。 我认为应该考虑强制执行该标准。
A+ 承诺已被选为 ES6 的首选。 我建议使用 polyfill 以确保支持 ES6 不兼容的节点版本(例如 https://github.com/jakearchibald/es6-promise)。

它应该在其他 Promise 库的手中,以便与 ES6 Promise 兼容和混合。

编辑:
当前的 API 不会中断,还是我遗漏了什么?

Promises/A+ 规范与 ES6 Promises 规范非常不同,后者又与我在之前的评论中列出的 Promise 库不同。 虽然如果将无数的 promise 库都整合到 ES6 中对我来说是件好事,但我怀疑这是否会发生,因为开源的美妙之处在于,一些热爱 promise 的人会想要额外的特性并且会编写自己的 promise图书馆。

当前 API 没有重大变化,我在想的是一种说法mongoose.set('Promise', require('bluebird'));或类似的东西,所以它是一个选择加入,而 mpromise 将是默认值。

哦,对了,对不起那个错误。
我查看了当前的实现,以及其他的 Promise 库。

我想我可以做这样的事情:

mongoose.set('Promise', Promise);

mongoose.set('Promise', require('bluebird'));

mongoose.set('Promise', require('q').defer());

mongoose.set('Promise', require('when').defer());

// and so on...

因此,基本上,您应该向 Mongoose 公开您的承诺选择对象,该对象具有resolvereject方法。

这会是你的想法吗? 如果是这样,我将处理拉取请求。

编辑:
mongoose.set('Promise', Promise);感觉非常愚蠢。 我认为 ES6 应该是默认的,可以使用您选择的库(并且在 ES6 Promises 不可用时做出承诺)。

当然,我会很感激你的帮助。 棘手的一点是 1) 让它与钩子一起工作 - 请参阅 vkarpov15/kareem#2,以及 2) 使其与 mpromise 向后兼容。

另外,关于:Q,我们将使用require('q').Promise ,因为这是最接近 ES6 规范的 Q 语法

感谢您的投入,将努力解决。 你更喜欢有竞争力的拉取请求还是正在进行的 PR?

完成更好,但我总是会有一些建议。 如果您遇到困难,请告诉我

+1 此功能。 我想和蓝鸟一起使用

很抱歉过于激烈 - 但另一种方法是完全弃用承诺支持。 这将使任何使用承诺请求其他库支持的人闭嘴。
使用像 bluebird 这样强大的 Promise 库的人无论如何都可以继续使用它,因为您公开了一个回调 API,而 bluebird 可以以 0 成本简单地包装它 - 事实上,由于您不知道或不使用 Promise(如您所说),机会是无论如何手动支持它们会更慢更容易出错。

@benjamingr 100% 不同意。 现在随着 iojs 和 NodeJs 合并到 Node 3.0 中,将支持 ES6 Promises 和 Generators。

放弃支持将是一个巨大的倒退。

也就是说, Promise.promisifyAll(require("mongoose"))为 Mongoose 创建了一个快速(比任何可能的手动尝试更快)的包装器,它符合标准,并且围绕所有 API,而您无需对其进行任何操作。 实际上,您可以自己对导出对象执行Promise.promisifyAll并免费公开 bluebird promise 和双重 promise 方法(save - saveAsync) ,然后说您无需执行任何实际工作即可公开 bluebird promise 接口。

所以,虽然我自己使用 bluebird 承诺 - 我认为这整个事情可以通过文档中的一个部分来修复,而不是通过对两个接口进行编码来使您的代码复杂化。

@albertorestifo除了实际上不会丢弃任何东西。 我非常清楚承诺是什么(堆栈溢出中超过 1500 分和 500 个答案:P),我什至负责它们在 io.js 中的工作方式的某些部分(例如 https://github.com/ nodejs/io.js/issues/256)。

这并没有改变我相信由于@vkarpov15不使用承诺,他不应该在他的库中支持它们的事实——尤其是因为无论如何使用承诺并没有带来任何好处。 即使不提供承诺,您也可以轻松地将承诺与猫鼬一起使用 - 猫鼬在内部实现支持更需要维护,可能更慢且更容易出错。 @vkarpov15可以继续处理回调 API,并且承诺用户无论如何都可以用简单的一行来包装 Mongoose。

@benjamingr假设一个人使用外部 Promise 库。 我坚持规范中的那个,你很清楚,它没有包装。

我明白你的意思。 支持两者会造成巨大的混乱。 我个人鄙视回调,所以我会放弃那些。 也许应该有两个独立的存储库,一个使用生成器和承诺,另一个使用回调。 两者都将保持相同的结构和 API 尽可能接近。

@benjamingr假设一个人使用外部 Promise 库。 我坚持规范中的那个,你很清楚,它没有包装。

如果您选择使用缓慢、难以调试且功能较少的实现,这当然是您的选择:P 但它与包装器有什么关系? 使用原生 Promise* 编写类似于 bluebird 的promisifyAll的包装器非常容易。

如果你想要一个支持承诺的 Mongoose 版本作为一个单独的包,你可以这样做:

  • 第 1 步,打开您最喜欢的编辑器,或者只是一个您可以接受的编辑器。
  • 第 2 步,输入module.exports = require("bluebird").promisifyAll(require("mongoose"))
  • 第 3 步,创建适当的package.json文件,发布到 npm
  • 第四步,上万次下载。

现在,我知道你在想什么“这不是使用原生承诺”,你仍然可以从承诺原型和all中删除除thencatch之外的所有方法allrace来自Promise并最终得到相同的 API - 或者你可以告诉人们你正在导出本地承诺 - 他们不会知道,因为它只是两个 Promises/A+ 实现,我承诺 ;)

(*在用户级别上快速编写它更难,因为现在没有快速的方法来创建 Promise,这就是为什么 io.js 最终可能会导出一个 Promisify 函数本身 - 这就是说通过采用一个 Promise 构造函数,你强迫它无论如何要慢)。

这绝对是一个不错的选择。 不过,我想在内部信守承诺,因为不管你喜不喜欢,这就是用户使用猫鼬的方式,所以我们不妨对其进行可靠的测试覆盖,所以我们可以指出并说“这就是您将 promise 库 X 与猫鼬一起使用”。 当您将事物断开到单独的模块中时,缺点是很难说“好吧,这个版本的 mongoose-promises 只适用于 mongoose 3.8,这个版本适用于 mongoose >= 4.1”,而且 mongoose 很难避免破坏一个总体承诺包装器.

这绝对是一个不错的选择。 不过,我想在内部信守承诺,因为不管你喜不喜欢,这就是用户使用猫鼬的方式,所以我们不妨对其进行可靠的测试覆盖,

为什么你想要/需要它的测试覆盖率? 在你的代码中测试 promise 本身是没有意义的——库已经有测试——这就像在你使用它时测试http模块。

并且 mongoose 很难避免破坏一个包罗万象的 Promise 包装器。

Bluebird 做了一些非常简单的事情 - 它找到原型,然后向它们添加带有Async后缀的方法 - 这非常简单,并且在实践中运行良好 - 它是包含 Mongoose 在内的大多数库的单线,并且没有破坏在过去的一年里对我来说甚至一次。

我不确定你为什么要维护大量可能容易出错的拼接代码,手动必须支持两个带有边缘情况的 API 听起来需要做很多工作,而且你可以随时破坏东西 - 你可以“借用”bluebird 的 promisifyAll 代码并将其调整为与其他 Promise 库一起使用(毕竟它是开源的),但我当然不会手动执行。

“好吧,这个版本的 mongoose-promises 只适用于 mongoose 3.8,这个版本适用于 mongoose >= 4.1”,而且 mongoose 很难避免破坏一个总体承诺包装器。

好吧,你介意给我一个例子,说明如果承诺是自动的,就必须发生重大变化吗?

1)我想测试用户使用它们的方式。

2)我很想偷懒并避免它,但据我了解,大多数其他承诺库都没有 promisifyAll 等价物。 我认为这就是为什么“支持 Xpromise 功能 Y”是最受欢迎的猫鼬功能请求的原因。 此外,我们无意重写 Bluebird 的 Promisification,只是让 mongoose 函数原生返回一个 Promise。

3)取决于promisification的实现以及你如何使用它:)

1)我想测试用户使用它们的方式。

测试用户使用它们的方式是什么意思? 你能给我看一个回调的类比吗?

2)我很想偷懒并避免它,但据我了解,大多数其他承诺库都没有 promisifyAll 等价物。 我认为这就是为什么“支持 Xpromise 功能 Y”是最受欢迎的猫鼬功能请求的原因。 此外,我们无意重写 Bluebird 的 Promisification,只是让 mongoose 函数原生返回一个 Promise。

您不必重写,您可以接受它 - 它不是通用的,因为通用实现会更慢。

不手动实现广泛可用的功能也不是懒惰。 NodeJS 是否因为没有将 express 放入核心而懒惰? TC39 是否因为不在核心中添加下划线而懒惰? 通过遵守约定(回调),您可以让用户使用他们想要的任何并发原语。

3)取决于promisification的实现以及你如何使用它:)

好吧,bluebird 的承诺,或者 Q 或 When's - 它们在实现上有所不同,但它们都做同样的事情 - 所以选择你喜欢的一个。 我只是想知道它会如何破裂。

我在这里的讨论中缺少一件事:

如果 mongoose 返回一个标准的 Promise(标准意味着 ES6 原生实现),它不应该与任何 ES6 兼容的 Promise 库兼容吗? 我可以做Promise.all([model.query().exec(), ...])就好了,还有蓝鸟,q,当等价的时候。

那么,为什么不返回回调和标准 Promise(就像它现在正在做的那样,但是“摆脱” mpromise)并让用户使用它最喜欢的 Promise 库? 或者我在这里错过了什么?

@albertorestifo好吧,本机承诺目前很慢,而且需要时间,可能需要数年才能与用户空间库达到同等水平 - 主要是这样。

@benjamingr你提出了一些好观点。 mongoose 承诺的主要目的是使您能够在没有任何其他库的情况下将yield与 mongoose 异步操作一起使用,这就是我们在可预见的未来信守承诺的原因。 IMO 这确实应该成为未来猫鼬核心的一部分。

Q 或When 是否具有承诺能力?

Q 或When 是否具有承诺能力?

是的,我所知道的几乎每一个广泛使用的承诺库都提供了某种承诺:

这是什么时候: https ://github.com/cujojs/when/blob/master/docs/api.md#nodeliftall
这是问: https ://github.com/kriskowal/q/wiki/API-Reference#qnfbindnodefunc -args

原生 Promise 还没有它,但它正在开发中——一旦存在创建 Promise 的快速路径(即 - 不是 Promise 构造函数),那么 NodeJS 可能会在原生 Promise 的核心中支持它(因为它可以' t 在用户空间中_fast_ 完成)。

mongoose 承诺的主要目的是使您能够在没有任何其他库的情况下将 yield 与 mongoose 异步操作一起使用

无论如何,您需要一个库才能以有意义的方式使用yield和承诺。 如果您可以自己编写将生成器泵为异步函数的 9 LoC,那么您绝对可以编写 promisify - 如果您像大多数用户一样,无论如何您都可以使用库。

我肯定看到想要/需要在 Mongoose 中允许承诺,它们是前进的方向以及语言现在如何进行并发,但老实说,我认为逐个方法手动执行会很痛苦。 在文档中的“与承诺一起使用”或“与生成器一起使用”部分中演示如何使用库可能会有所帮助。

问题是我们已经逐个方法完成了,我们只需要修改内部的 Promise 包装器。 不管怎样,在 5.0 之前我们都不能完全从核心中移除 Promise。 我同意弃用它的想法, @benjamingr确实提出了一些很好的论据,我必须仔细考虑,但我认为下一步无论如何都是#2688。

你显然对 Mongoose 有更多的经验,更重要的是它的用户——所以我理解这种选择。 非常感谢你听我说完。

我输入了“its”,但我的 iPhone 觉得它应该自动更正为“it's”,而 GitHub 认为编辑评论不是一个有趣的用例。 对不起,双重评论垃圾邮件:)

如果您单击评论右上角的铅笔,您可以在网站上编辑评论。

我总是乐于进行一场热烈的辩论,尤其是教会我一些新东西的辩论:啤酒:我稍后可能会联系你讨论更多:)

未来似乎属于原生承诺。 Mongoose 是 mongo ops 的行业标准,它完全可以依赖标准的 Promise。

标准 Promise 将不可避免地成熟并变得比任何框架都更广泛。 使用它们的谦虚+1。

@iliakan也许在未来。 最早要到 2016 年年中,我才期望原生 Promise 成为“标准”,各种零散的 Promise 库根深蒂固,有太多微妙的怪癖。 无论哪种方式,猫鼬都无法切换到“默认情况下本机”,而无需进行大规模的向后破坏性更改。

@vkarpov15我当然明白。

同时有什么好处 - 一个简单的页面,概述了 mpromise 和 native promise 之间的主要不兼容性。 至少你不应该用mpromise尝试的东西;)

我是否明白现在没有catch ,这就是所有的限制?

不知道,还没有真正深入研究过 ES6 API。 mpromise 实现了 Promises/A+没有别的,这意味着没有 .catch(),没有长堆栈跟踪等等。基本上,任何不是.then()的东西都超出了 mpromise 的实现范围。

一般来说,实现 Promises/A+ 的东西从高层的角度也实现了 ES6 的 Promise,但反之则不然。 Promises/A+ 对低级实现细节非常具体,例如,bluebird 不完全遵守 Promises/A+,我不确定其他常见的 Promise 库,但我确信它们不遵守规范自己独特的方式。 这就是让这件事变得特别棘手的原因。

你的意思是,现在可以使用 bluebird 和 native Promise 了吗?

是的require('mongoose').Promise = global.Promise将使猫鼬使用本机承诺。 你应该可以使用任何 ES6 Promise 构造函数,但现在我们只使用原生、bluebird 和 Q进行测试

@vkarpov15这太棒了! 太感谢了!

@vkarpov15 非常感谢! 做得好!

是的,绝对的。 这太酷了! :)

@vkarpov15这真的很好

此页面是否有帮助?
0 / 5 - 0 等级