web-dev-qa-db-ja.com

Qを使用してnode.jsプロミスチェーンを適切に中止するにはどうすればよいですか?

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();
}

基本的にこれは機能するようです。タスクステップのいずれかによってエラーがスローされた場合、それはコールバックに渡されます(ただし、node.jsの約束は初めてなので、改善を歓迎します)。ただし、タスクチェーンを早期に中止する必要がある場合、問題が発生します。たとえば、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();
}

この例では、「中止」の両方が表示されます。 「ステップ3を実行中...」が印刷されます。

ここでいくつかの基本的な原則を誤解しているに違いないので、助けていただければ幸いです。ありがとう!

35
Zane Claes

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();
}
17
Calvin Alvin

これは、分岐する必要がある場合です。つまり、ネストするかサブルーチンを作成します。

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)
}

または

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);
    })
}
37
Kris Kowal

私はあなたが約束の連鎖から抜け出す約束を拒否するだけでよいと信じています。

https://github.com/kriskowal/q/wiki/API-Reference#qrejectreason

また、.end()が.done()に変更されたようです

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!');
            // by calling Q.reject, your second .then is skipped,
            // only the .fail is executed.
            // result1 will be passed to your callback in the .fail call
            return Q.reject(result1);
        }
        return Q.ncall(task.step2, task);
    })
    .then(function(result2){
        console.log('doing step 3...');
        return Q.ncall(task.step3, task);
    })
    .fail(callback).done();
}
2
George