如何使用Q正确中止node.js承诺链?

Zan*_*aes 35 node.js promise q

我正在使用Node.js 的Q模块试图在我有很多步骤的情况下避免"厄运金字塔".例如:

function doTask(task, callback)
{
    Q.ncall(task.step1, task)
    .then(function(result1){
        return Q.ncall(task.step2, task);
    })
    .then(function(result2){
        return Q.ncall(task.step3, task);
    })
    .fail(callback).end();
}
Run Code Online (Sandbox Code Playgroud)

基本上这似乎工作; 如果任何任务步骤引发错误,它将被传递给回调(尽管我欢迎改进,因为我是node.js promises的新手).但是,当我需要尽早中止任务链时,我遇到了问题.例如,如果result1成功返回,我可能想提前调用回调并中止其余的回调,但我尝试这样做的失败...

function doTask(task, callback)
{
    Q.ncall(task.step1, task)
    .then(function(result1){
        if(result1)
        {// the rest of the task chain is unnecessary 
            console.log('aborting!');
            callback(null, result1);
            return null;
        }
        return Q.ncall(task.step2, task);
    })
    .then(function(result2){
        console.log('doing step 3...');
        return Q.ncall(task.step3, task);
    })
    .fail(callback).end();
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我看到两个"中止!" 和"做第3步......"打印.

我相信我只是在这里误解了一些基本原则,所以我将不胜感激.谢谢!

Kri*_*wal 38

这是您需要分支的情况,这意味着嵌套或创建子例程.

function doTask(task, callback) {
    return Q.ncall(task.step1, task)
    .then(function(result1) {
        if (result1) return result1;
        return Q.ncall(task.step2, task)
        .then(function(result2) {
            return Q.ncall(task.step3, task);
        })
    })
    .nodeify(callback)
}
Run Code Online (Sandbox Code Playgroud)

要么

function doTask(task, callback) {
    return Q.ncall(task.step1, task)
    .then(function(result1) {
        if (result1) {
            return result1;
        } else {
            return continueTasks(task);
        }
    })
    .nodeify(callback)
}

function continueTasks(task) {
    return Q.ncall(task.step2, task)
    .then(function(result2) {
        return Q.ncall(task.step3, task);
    })
}
Run Code Online (Sandbox Code Playgroud)

  • @SvenJacobs您在该示例中描述的内容是异常的一个好例子.考虑https://gist.github.com/kriskowal/e98774443eb0f1653871 (4认同)
  • 我仍然遇到这种方法的问题,因为它使错误处理更加困难.投入一个承诺链(Calvin Alvin的回答)允许有一个`.fail()`来处理流程中的任何错误.以这种方式写承诺(分支)让我回到了回调地狱. (3认同)

小智 17

在promise链中抛出的任何错误都将导致整个堆栈提前中止,并将控制权交给错误返回路径.(在这种情况下,fail()处理程序)当您检测到某个导致您想要中止promise链的状态时,只需抛出一个非常特定的错误,您将在错误返回中陷入并忽略(如果您这样做)选择)

function doTask(task, callback)
{
    Q.ncall(task.step1, task)
    .then(function(result1){
        if(result1 == 'some failure state I want to cause abortion')
        {// the rest of the task chain is unnecessary 
            console.log('aborting!');
            throw new Error('abort promise chain');
            return null;
        }
        return Q.ncall(task.step2, task);
    })
    .then(function(result2){
        console.log('doing step 3...');
        return Q.ncall(task.step3, task);
    })
    .fail(function(err) {
        if (err.message === 'abort promise chain') {
            // just swallow error because chain was intentionally aborted
        }
        else {
            // else let the error bubble up because it's coming from somewhere else
            throw err;
        } 
    })
    .end();
}
Run Code Online (Sandbox Code Playgroud)

  • 您正在使用控制流的例外,通常不建议这样做.Kris Kowal给出的解决方案避免了这个问题. (19认同)
  • `throw`之后不需要`return null` (4认同)