如果你想要半小时视频形式的 Promise 介绍,我建议我在 JSConf.eu 发表演讲:http: //youtu.be/qbKWsbJ76-s
我从您的问题中得到的印象是您以前没有真正看过承诺(以及其他一些不是其他人都有的答案)。
JavaScript 承诺在Promises/A+规范中定义。它非常容易阅读,因此值得一看。并非所有承诺都遵循此规范(jQuery 是唯一真正值得注意的例外),但您可以在此处找到兼容实现的列表
作为新人:
如果你想看看 Promise 是如何实现的,Promise是最简单的一种(它也是最快的性能之一)。免责声明:我写了这个
不过,Promise 非常有限,因此如果没有大量扩展,它就没有多大用处。我建议您从Q开始,因为它是最受欢迎的之一,并且具有许多使其使用起来更加简单的功能。Q 也最适合未来规范中的大多数想法。
如果您使用 Mongoose 作为您的数据库驱动程序,它带有自己的 Promises/A+ 实现,因此您可以使用它:
access = function(id, user_id) {
return docs.findOne({
_id: id
})
.then(function(doc) {
if (doc.user.indexOf(user_id) == -1) {
throw new Error('User Not Found')
}
});
}
这里发生的docs.findOne
是返回一个“承诺”,因为它是在没有回调的情况下调用的。附加的处理程序.then
添加了一个回调。它还返回一个新的 Promise,第一个 Promise 中的任何错误都会自动传播到新的 Promise。如果在回调中抛出错误,则新的承诺被“拒绝”。如果回调返回一个值,那么新的 Promise 将用该值“实现”。
像这样定义了我们的函数后,我们可以调用它:
access(id, user_id)
.then(function () {
doSomething();
});
如果在某个时候我们想要处理该错误,我们可以通过附加错误处理程序来实现:
access(id, user)
.then(function () {
doSomething()
}, function (err) {
doSomethingElse()
})
唯一的问题是,如果doSomething
抛出错误,它将被静音(这很糟糕)。如果您完成了将.then
调用链接到一个承诺,您需要结束该链。
如果您使用的是 mongoose 内置的 Promise,那么您可以调用end
access(id, user)
.then(function () {
doSomething()
}, function (err) {
doSomethingElse()
})
.end();
如果您使用的是 Q 承诺,那么您将调用完成
access(id, user)
.then(function () {
doSomething()
}, function (err) {
doSomethingElse()
})
.done();
如果你有一个想要转换为 Q 承诺的猫鼬承诺,你可以简单地将它传递给Q
:
Q(access(id, user))
.then(function () {
doSomething()
}, function (err) {
doSomethingElse()
})
.done();
如果你不使用猫鼬
如果您不使用 Mongoose,并且需要从头开始创建 Q 承诺,请使用Q.promise
var promise = Q.promise(function (resolve, reject) {
docs.findOne({
_id: id
}, function(err, doc) {
if (doc.user.indexOf(user_id) != -1) {
resolve("authenticated")
} else {
reject(new Error("User Not Found"));
}
});
})
还有一个处理节点样式 API 的快捷方式:
var findOne = Q.denodeify(docs.findOne);
var promise = findOne({_id: id});
设计推理
最后,如果你想知道为什么 Promise 会以它们的方式工作,或者你认为它们的设计在某些方面很愚蠢,那么https://github.com/kriskowal/q/blob/master/design/README.js是一个很好的指南. 它会带你从头开始开发一个 Promise 库,从你认为“回调还不够好”的地方开始,一直到一个完整的 Promise 库。