使用Q / promises与回调

时间:2013-12-06 18:01:29

标签: node.js q

我在nodejs中使用Q库并且过去没有对promises工作太多,但是我有半复杂的逻辑需要大量的嵌套,并且认为Q是一个很好的解决方案,但是我发现它似乎与“回调地狱”几乎相同。

基本上我说有5种方法,都需要前一种或前一种方法的数据。这是一个例子:

我们从一些二进制数据开始,这些数据具有基于二进制生成的sha1哈希。

var data = {
    hash : "XXX"
  , binary: ''
}

首先,我们希望看看我们是否已经使用此方法:

findItemByHash(hash)

如果我们没有,我们需要保存它,使用:

saveItem(hash)

现在我们需要将其与用户相关联,而不仅仅是保存的结果。现在我们关联的层次结构要大得多,所以我们首先需要做到这一点:

getItemHierarchy(item_id),我们使用之前item_id

返回的saveItem

现在,我们可以将这些结果“复制”给用户:

saveUserHierarchy(hierarchy)

现在我们已经完成了,但是,假设该项目尚未存在。所以我们需要处理项目确实存在的情况。这将是:

我们需要检查用户是否有这个:

getUserItemByItemId(item_id) - item_id已从findItemByHash

返回

如果存在,我们就完成了。

如果没有:

getItemHierarchy(item_id)

然后

saveUserHierarchy(hierarchy)

好的,现在我们有回调来做这些检查,这很好。但是我们需要在整个过程中处理每种情况下的错误。这也很好,只是增加了混乱。实际上,如果流的任何部分抛出错误或拒绝,那么它可以停止并在一个地方处理它。

现在有了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中有没有更好的方法来处理这个问题?

谢谢!

1 个答案:

答案 0 :(得分:4)

我喜欢承诺的原因之一是处理错误是多么容易。在您的情况下,如果其中任何一个promise失败,它将被您定义的fail子句捕获。如果您想在现场处理 ,可以指定更多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);
});

为了进一步减少它,制作组合重复承诺组合的函数,我们留下:

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 promises,但原则是相同的。