Правильный способ пропустить функцию then в Q Promises

В моем коде, основанном на определенном условии, я хотел бы перейти к функции done, независимо от всех функций then.

Оригинальная версия этого вопроса находится в редактировании. Ниже приведена фактическая проблема, с которой я имею дело. Извините за неудобства

Актуальная проблема:

Я читаю файл и обрабатываю его. Если содержимое файла соответствует определенным условиям, я должен выполнить ряд операций в файловой системе (скажем, прочитать и записать несколько файлов), а затем выполнить функцию done. Если условия не работают, , я должен пропустить весь ряд операций, и я должен выполнить функцию done напрямую.

Я возвращаю объект (скажем result) во всех функциях then, а в следующем then обновляю result и возвращаю его. Итак, когда все then выполнены, done будет иметь накопленный result. Наконец, done обработает result и распечатает его.

Итак, если изначально условия не выполняются, done просто напечатает result (который будет пустым).

Q()
.then(readFile)
.then(function (contents) {
    var processResult = process the contents;
    if (processResult) {
        return {};
    } else {
        // How can I skip to `done` from here
    }
})
.then(function (results) {
    // do some more processing and update results
    return results;
})
...   // Few more then functions similar to the one above
...
.fail(function (exception) {
    console.error(exception.stack);
})
.done(function (results) {
   // do some more processing and update results
   console.log(results);
});

Ответ 1

Это немного зависит от того, какие условия пропускать, какие операции вы делаете и как "полезно" все это, когда условия не удались. Вы можете использовать интеллектуальное отклонение здесь, чтобы передать сообщение. В противном случае я считаю, что правильный способ справиться с этим - это действительно вложенный набор обещаний.

Это также соответствует основной идее promises, которая заключается в возврате синхронных структур управления к выполнению асинхронного кода. В общем, при использовании promises вы должны сначала подумать о том, как выполнить задачу с синхронным кодом. И если вы подумаете о своей ситуации, это, вероятно, будет работать следующим образом:

var contents = readFromFile();
var results = initialOperation(contents);
if (fancyCondition(results)) {
     results = doSomething(results);
     results = doMore(results);
}
processAndPrint(results);

Итак, у вас будет реальная ветка в синхронном коде. Таким образом, нет смысла, что вы хотели бы избежать этого в асинхронном коде с помощью promises. Если бы вы могли просто пропустить что-то, вы, по сути, использовали прыжки с gotos. Но вместо этого вы отключаетесь и делаете что-то другое.

Итак, вернемся к promises и асинхронному коду, имеющему фактическую ветвь с другим набором цепочечных операций, полностью точным и актуальным в духе намерения позади promises. Таким образом, выше код может выглядеть так:

readFromFile(fileName)
.then(initialOperation)
.then(function (results) {
    if (fancyCondition(results) {
        return doSomething(results)
            .then(doMore);
    }
    return results;
})
.catch(errorHandler)
.then(processResults)
.then(outputResults); // Or `done` in Q

Также обратите внимание, что при использовании функций, возвращающих promises самостоятельно, вместо того, чтобы создавать их inline из then, обещающий конвейер автоматически выглядит намного более чистым.

Ответ 2

Но тогда мы вложим последующие функции. Это то, чего мы хотели избежать в первую очередь с помощью promises.

Нет, это действительно правильный способ. Для ветвления вам всегда понадобится дополнительный уровень гнездования. Если углубление становится слишком большим, вы все равно можете применить старый трюк вызова подпроцедур (который также используется для отключения функций обратного вызова).


Другие решения довольно уродливые. Пропуск нескольких then -сборщиков в цепочке, без глубокого вложения, можно сделать, бросив исключение и отвергнув обещание; это может быть пойман в конце концов. Это может быть применимо к нескольким сценариям, но я бы не счел это хорошей идеей.

Другим способом, который я мог бы подумать, будет обертка условного результата в другую структуру данных, которая может быть передана через цепочку then s. Это было бы как Maybe в Haskell или Option в Scala, и вы бы map над ними в обработчиках. Однако для этого также потребуется дополнительный уровень вложенности, было бы менее эффективно явно не передавать ничего через цепочку и было бы проблемой с возвратом promises в цепочке.

Ответ 3

Если я правильно понимаю "пропустить", то общее решение не должно возвращать значение в условиях "пропустить", таким образом позволяя входное значение проходить прозрачно.

например:

...then(function (randomInteger) {
    var remainder = randomInteger % 2;
    console.log(['Even','Odd'][remainder] + ' number: ', randomInteger);
    if(remainder) {
        return randomInteger + 1;
    }
})...

Ответ 4

Я использую контекстную переменную и реализую условную логику в функциях, называемых в цепочке обещаний. Каждая функция возвращает одну и ту же контекстную переменную. Это держит цепочку обещаний аккуратной. Использование имен функций, которые указывают на условные тесты, улучшает читаемость.

function processFirstPass(context) {
    var processResult = process the contents;
    if (processResult) {
        context.result = processResult;
    }
    context.specialTag = ! processResult;
    return context;
}

processForOnlySpecialTag(context) {
    if (context.specialTag) {
        context.result = // do some more processing and update results
    }
    return context;
}

function process() {
    var context = { filename: 'input.txt' };
    readFromFile(context)
    .then(procesFirstPass)
    .then(processForOnlySpecialTag)
    .fail(function (exception) {
        console.error(exception.stack);
    })
    .done(function (context) {
       // do some more processing and update results
       console.log(context.result);
    });
}