Повторите шаг обещания

Предположим, у меня есть следующая цепочка обещаний:

var result = Promise.resolve(filename)
    .then(unpackDataFromFile)
    .then(transformData)
    .then(compileDara)
    .then(writeData);

Теперь у меня есть не только одинtransformData Функция, но две или более, хранятся в массиве. Я хочу попробовать первый, и еслиcompileData функция не работает, попробуйте второй и так далее, пока либоcompileData успешно или массивtransformData функции исчерпаны.

Может кто-нибудь дать мне пример, как это реализовать?

Работает всеtransformData функции и дать массив результатовcompileData это не вариант, так как функции очень дороги, и я хочу запустить как можно меньше из них.

transformData Сам также возвращает Обещание, если это помогает.

Ответы на вопрос(3)

var transformers = [transformData, transformData2];

var result = unpackDataFromFile(filename)
  .then(function transpile(data, i = 0) {
    return transformers[i](data).then(compileData)
      .catch(e => ++i < transformers.length? transpile(data, i) : Promise.reject(e));
  })
  .then(writeData);

В основном вы рекурсивны на массиве трансформаторов, используя.catch().

 jib04 июл. 2016 г., 19:43
@ LUH3417 Продано. Обновленный ответ (нам не нужноreduce больше тоже нет).
 user644553304 июл. 2016 г., 15:24
ОП имеет несколько преобразователей, но только один compileData. Он должен был создать этот вложенный массив в первую очередь. Почему не простоfunction transpile(data, i = 0) { return [transformers[i], compileData].reduce((p, f) => p.then(f), Promise.resolve(data)).catch(e => i < transpilers.length && transpile(data, i + 1)); }, Эта адаптация также позволяет избежать мутации глобального состояния.

// various transformer functions to try in order to be tried
var transformers = [f1, f2, f3, f4];    

function transformFile(filename) {
    // initialize tIndex to select next transformer function
    var tIndex = 0;
    var p = unpackDataFromFile(filename);

    function run() {
          return p.then(transformers[tIndex++])
          .then(compileData)
          .catch(function(err) {
              if (tIndex < transformers.length) {
                // execute the next transformer, returning
                // a promise so it is linked into the chain
                return run();
              } else {
                // out of transformers, so reject and stop
                throw new Error("No transformer succeeded");
              }
          }).then(writeData);

    }
    return run();
}

transformFile("someData.txt").then(function(finalResult) {
    // succeeded here
}).catch(function(err) {
    // error here
});

Вот как это работает:

УстанавливаетtIndex переменная, которая индексирует в массив функций преобразователя.ВызовыunpackDataFromFile(filename) и сохраняет полученное обещание.Затем выполняет последовательностьp.then(transformer).then(compileData) используя первый трансформатор. Если это удается, он вызывает writeData и возвращает полученное обещание.В случае сбоя преобразователя или compileData он переходит к следующей функции преобразователя и запускается заново. Ключом к созданию этой работы является то, что в.catch() обработчик, он возвращает новое обещание, которое связывается с первоначально возвращенным обещанием. Каждый новый звонокrun() прикован к первоначальному обещанию отunpackDataFromFile() что позволяет вам повторно использовать этот результат.

Вот немного более общая реализация, которая делает итератор для массива, который повторяется, пока обратный вызов итератора не возвращает обещание, которое выполняет.

// Iterate an array using an iterator that returns a promise
// Stop iterating as soon as you get a fulfilled promise from the iterator
// Pass:
//    p - Initial promise (can be just Promise.resolve(data))
//    array - array of items to pass to the iterator one at a time
//    fn - iterator function that returns a promise
//         iterator called as fn(data, item)
//             data - fulfilled value of promise passed in
//             item - array item for this iteration
function iterateAsyncUntilSuccess(p, array, fn) {
    var index = 0;

    function next() {
        if (index < array.length) {
            var item = array[index++];
            return p.then(function(data) {
                return fn(data, item).catch(function(err) {
                    // if this one fails, try the next one
                    return next();
                });
            });
        } else {
            return Promise.reject(new Error("End of data with no operation successful"));
        }
    }

    return next();
}

// Usage:
// various transformer functions to try in order to be tried
var transformers = [f1, f2, f3, f4];    

iterateAsyncUntil(unpackDataFromFile(filename), transformers, function(data, item) {
    return item(data).then(compileData);
}).then(writeData).then(function(result) {
    // successfully completed here
}).catch(function(err) {
    // error here
});
 user644553304 июл. 2016 г., 13:40
Несомненно, эта реализация более эффективна, чем у Торазабуро. Но вместо двух функций у нас теперь есть одна очень специфическая функция и, следовательно, возможность повторного использования.
 user644553304 июл. 2016 г., 19:22
Это не было проблемой вообще: D. Теперь выглядит лучше. Не могу голосовать, потому что я уже сделал.
 user644553304 июл. 2016 г., 17:34
Нет, нет, я думаю, что это просто недоразумение. Ваше решение, очевидно, лучше. Я бы просто попытался разделитьtransformFile на более мелкие функции, поскольку в настоящее время они очень специфичны и предназначены именно для данной задачи.
 jfriend0004 июл. 2016 г., 17:23
@ LUH3417 - Извините, я перепутал вас с ОП. Это не совсем микрооптимизация, чтобы избежать чтения и обработки множества ненужных файлов на сервере. Это может быть реальная значимая работа для сервера, пытающегося обрабатывать запросы от множества пользователей, и это часто является важным требованием. Избегать чтения и обработки целых файлов - это не совсем микрооптимизация.
 user644553304 июл. 2016 г., 17:10
Я не ОП и прошу прощения за то, что не прочитал вопрос полностью. Вы правы. Лично мне не нравится такая микрооптимизация.
 jfriend0004 июл. 2016 г., 19:03
@ LUH3417 - Я взял на себя вашу задачу и создал более общее решение итератора.
 jfriend0004 июл. 2016 г., 17:27
@ LUH3417 - Если бы я дал вам 20 имен файлов по порядку и попросил вас эффективно вернуть первый файл, который соответствует определенным критериям (который требует чтения и обработки файла), вы бы написали одну функцию, запустили ее для всех 20 файлов и затем изучили результаты, чтобы найти первый, который соответствует критериям, или вы запустите его на первом, проверьте результаты, если он не соответствует критериям, затем запустите его на следующем и т. д.?
 jfriend0004 июл. 2016 г., 17:02
@ LUH3417 - Да, но вы перечислили требованиетак как функции очень дороги, и я хочу использовать как можно меньше из них. и реализация torozaburo выполняет все функции все время, даже если самая первая из них завершается успешно. Этот запускает функцию за раз и останавливается, когда первый завершается успешно. Вероятно, можно сделать эту концепцию более общей, если это также является требованием. Извините, но я просто пишу самый быстрый код, который соответствует перечисленным вами требованиям. Я удивлен, если вы думаете, что код torazaburo соответствует вашему требованию, чтобы запустить как можно меньше функций.

что выделю понятие попытки выполнить несколько обещаний, пока одно из них не будет успешным:

function tryMultiple([promise, ...rest]) {
  if (!promise) throw new Error("no more to try");
  return promise.catch(() => tryMultiple(rest));
}

Теперь напишите обработчик, который пробует каждую комбинацию преобразования и компиляции:

function transformAndCompile(transformers) {
  return function(data) {
    return tryMultiple(transformers.map(t => t(data).then(compileData)));
  };
}

Теперь верхний уровень просто:

var result = Promise.resolve(filename)
  .then(unpackDataFromFile)
  .then(transformAndCompile(transformers))
  .then(writeData);

Кстати,Promise.resolve(filename).then(unpackDataFromFile) это просто окольный способ сказатьunpackDataFromFile(filename).

 user66303124 июн. 2016 г., 05:43
@ jfriend00 Я проведу несколько тестов, но «рекурсивный» вызовcatch предложение, так как он будет работать, если преобразователь (и компилятор) успешно?
 jfriend0024 июн. 2016 г., 05:57
Рекурсивный вызов - это просто проверка результатов - не там, где были начаты операции. Все они были начаты задолго до этого в.map().
 jfriend0024 июн. 2016 г., 04:07
Разве вы не выполняете каждый трансформер, даже если первый преуспеет?
 jfriend0024 июн. 2016 г., 05:52
Ты бежишьtransformers.map(), Это синхронно, поэтому он будет обрабатывать весь массив трансформаторов и вызыватьt(data).then(compileData) на всех из них синхронно, прежде чем вы получитеtryMultiple(), Затем вы будете один за другим изучать полученные обещания, пока не найдете то, что сработало, но ВСЕ операции были выполнены, и вы просто смотрите, чтобы увидеть, какое из них было выполнено успешно. Это отличается от выполнения одной операции, посмотрите, если это удастся. Если так, то все готово. Если нет, запустите следующий.

Ваш ответ на вопрос