使用Q / promise和callback
我在nodejs中使用Q库,过去没有太多的承诺,但我有半复杂的逻辑,需要大量的嵌套,并认为Q将是一个很好的解决scheme,但是我发现它似乎几乎就像“回拨地狱”一样。
基本上我已经说了5种方法,所有这些都需要上一个或之前的数据。 这是一个例子:
我们从一些二进制数据开始,基于二进制生成一个sha1哈希。
var data = { hash : "XXX" , binary: '' }
首先我们要看看我们是否已经有了这个,使用这个方法:
findItemByHash(hash)
如果我们没有它,我们需要保存它,使用:
saveItem(hash)
现在我们需要把这个关联到一个用户,而不仅仅是保存的结果。 现在有一个更大的层次结构,所以我们需要首先做到这一点:
getItemHierarchy(item_id)
,我们使用从我们以前的saveItem
返回的item_id
现在,我们可以将这些结果“复制”给用户:
saveUserHierarchy(hierarchy)
现在我们已经完成了,但是,这是假设该项目还不存在。 所以我们需要处理这个项目确实存在的情况。 这将是:
我们需要检查用户是否有这样的:
getUserItemByItemId(item_id)
– item_id从findItemByHash
返回
如果存在,我们就完成了。
如果没有:
getItemHierarchy(item_id)
然后
saveUserHierarchy(hierarchy)
好的,现在我们有callback做这些检查,这很好。 但是,我们需要在每种情况下处理错误。 这也没关系,只是增加了混乱。 真的,如果stream程的任何部分抛出错误或拒绝,那么它可以停下来,只是在一个地方处理它。
现在用Q,我们可以做这样的事情:
findItemByHash(hash).then(function(res) { if (!res) { return saveItem(hash).then(function(item) { return getItemHierarchy(item.id).then(function(hierarchy) { return saveUserHierarchy(hierarchy); }); }) } else { return getUserItemByItemId(res.id).then(function(user_item) { if (user_item) { return user_item; } return getItemHierarchy(res.id).then(function(hierarchy) { return saveUserHierarchy(hierarchy); }); }); } }) //I think this will only handle the reject for findItemByHash? .fail(function(err) { console.log(err); }) .done();
所以,我想我的问题是这样的。 在Q中有更好的方法来处理这个问题吗?
谢谢!
我喜欢承诺的原因之一是处理错误是多么容易。 在你的情况下,如果这些承诺中的任何一个都失败了,它将会被你定义的故障条款所捕获。 如果你想现场处理它们,你可以指定更多的fail
子句,但这不是必需的。
作为一个简单的例子,有时我想处理错误,并返回其他的东西,而不是传递错误。 我会做这样的事情:
function awesomeFunction() { var fooPromise = getFoo().then(function() { return 'foo'; }).fail(function(reason) { // handle the error HERE, return the string 'bar' return 'bar'; }); return fooPromise; } awesomeFunction().then(function(result) { // `result` will either be "foo" or "bar" depending on if the `getFoo()` // call was successful or not inside of `awesomeFunction()` }) .fail(function(reason) { // This will never be called even if the `getFoo()` function fails // because we've handled it above. });
现在,关于退出“返回地狱”的问题 – 只要下一个函数不需要关于前一个函数的信息,就可以链接.then
子句而不是嵌套它们:
doThis().then(function(foo) { return thenThis(foo.id).then(function(bar) { // `thenThat()` doesn't need to know anything about the variable // `foo` - it only cares about `bar` meaning we can unnest it. return thenThat(bar.id); }); }); // same as the above doThis().then(function(foo) { return thenThis(foo.id); }).then(function(bar) { return thenThat(bar.id); });
为了进一步减less它,使function组合重复承诺组合,我们留下:
function getItemHierarchyAndSave(item) { return getItemHierarchy(item.id).then(function(hierarchy) { return saveUserHierarchy(hierarchy); }); } findItemByHash(hash).then(function(resItem) { if (!resItem) { return saveItem(hash).then(function(savedItem) { return getItemHierarchyAndSave(savedItem); }); } return getUserItemByItemId(resItem.id).then(function(userItem) { return userItem || getItemHierarchyAndSave(resItem); }); }) .fail(function(err) { console.log(err); }) .done();
免责声明:我不使用Q承诺, when
承诺主要是为了它附带的额外的好处when
,我倾向于承诺 ,但原则是相同的。