Шаблоны Promise Retry Design

редактировать

Шаблон, который продолжает повторяться до разрешения обещания (с задержкой и maxRetries).Шаблон, который продолжает повторяться до тех пор, пока условие не будет соответствовать результату (с задержкой и maxRetries).Эффективный по памяти динамический шаблон с неограниченным количеством повторов (задержка при условии).

Код для № 1. Продолжает повторяться до разрешения обещания (какие-либо улучшения сообщества для языка и т. Д.?)

Promise.retry = function(fn, times, delay) {
    return new Promise(function(resolve, reject){
        var error;
        var attempt = function() {
            if (times == 0) {
                reject(error);
            } else {
                fn().then(resolve)
                    .catch(function(e){
                        times--;
                        error = e;
                        setTimeout(function(){attempt()}, delay);
                    });
            }
        };
        attempt();
    });
};

использование

work.getStatus()
    .then(function(result){ //retry, some glitch in the system
        return Promise.retry(work.unpublish.bind(work, result), 10, 2000);
    })
    .then(function(){console.log('done')})
    .catch(console.error);

Код для № 2 продолжайте попытки до тех пор, покаthen результат в многократном использовании (условие, что будет меняться).

work.publish()
    .then(function(result){
        return new Promise(function(resolve, reject){
            var intervalId = setInterval(function(){
                work.requestStatus(result).then(function(result2){
                    switch(result2.status) {
                        case "progress": break; //do nothing
                        case "success": clearInterval(intervalId); resolve(result2); break;
                        case "failure": clearInterval(intervalId); reject(result2); break;
                    }
                }).catch(function(error){clearInterval(intervalId); reject(error)});
            }, 1000);
        });
    })
    .then(function(){console.log('done')})
    .catch(console.error);
 shoover25 апр. 2017 г., 21:18
Это похоже на ответ. Есть вопрос?
 user272719506 июл. 2016 г., 01:13
@jfriend, что случилось с ответом, почему он был удален?
 user272719506 июл. 2016 г., 06:17
@torazaburo, пожалуйста, обратите внимание на сложный и чистый интерфейс / использование кода # 1 в моем вопросе
 user272719525 апр. 2017 г., 22:44
последующие изменения были моими попытками найти решение на самом деле
 user272719506 июл. 2016 г., 08:02
Я понимаю, что вы говорите о связывании с предыдущими, и я испытал преимущества, связанные с существующими обещаниями, работая над этим проектом, я знаю, что вы напишите мне несколько попыток, но, надеюсь, я смогу сформулировать сейчас, и вы поймете, что я ищу, любые правки к вашему ответу пожалуйста. и спасибо за указание ошибки интервала. да это не остановит
 user66303106 июл. 2016 г., 06:09
 user272719506 июл. 2016 г., 07:47
@ jfriend00 отлично. Спасибо за помощь в этом, я исправил. все хорошо для №2, пожалуйста?
 jfriend0006 июл. 2016 г., 08:02
В # 2 вы, похоже, ничего не делаете с результатом.then(work.getStatus), Я не могу сказать, почему это даже там.
 user272719506 июл. 2016 г., 08:04
вы можете игнорировать work.getStatus, это вызов API для получения статуса публикации, отдельный вызов после публикации, но вы можете проигнорировать. Я заинтересован только в повторении самым компактным способом, не закатывая глаза еще куда.
 jfriend0006 июл. 2016 г., 07:44
В коде № 2, еслиcollection.requestStatus(result) отвергает, вы ничего не делаете - это будет просто цикл навсегда. Если он получит какую-то повторяющуюся ошибку, вы никогда не решите или не отклоните. Если что-нибудь добавитcollection.requestStatus(result).then() обработчик, никто не захватывает это нигде. Неожиданные отклонения должны распространяться обратно, чтобы они могли быть обработаны.
 user66303106 июл. 2016 г., 06:08
Не добавляйте "правки" на ваш вопрос. Это делает его трудно следовать. Вместо этого просто отредактируйте свой вопрос. Если кто-то хочет посмотреть историю изменений, он может это сделать.
 user272719506 июл. 2016 г., 06:13
@torazaburo ваш ответ не то, что мне нужно, они используют внешние функции, которые разбросаны по всему коду. Пожалуйста, откройте снова, я пытаюсь найти образец здесь, а не какой-либо типичный ответ. и те ответы не улучшают мой код в любом случае.
 ShuberFu06 июл. 2016 г., 01:00
Не уверен, чтоsetInterval добьется внутреннего обещания, где оно решается?
 user272719506 июл. 2016 г., 06:09
Я думаю удалить это и начать новое. многое выяснилось в процессе.
 jfriend0006 июл. 2016 г., 07:50
Ваша неспособность справиться.catch() это ТОЧНАЯ ошибка, которая часто допускается при создании собственного нового обещания, а не просто приковки к предыдущему. Именно поэтому не рекомендуется создавать новое обещание, когда вам это не нужно. Я был на вашем месте раньше. Вы думаете, что это проще. Но это не лучше (это намного более подвержено ошибкам), и как только вы освоитесь с цепочкой, это станет еще проще.
 Dan Dascalescu31 мая 2017 г., 03:17
@ User2727195:пожалуйста рассмотреть возможность получения значимого имени пользователя.
 jfriend0006 июл. 2016 г., 08:00
В # 2, если вы нажмете.catch(reject)Вы не останавливаете свой интервал.

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

work.create()
    .then(work.publish) //remote work submission
    .then(function(result){
        var maxAttempts = 10;
        var handleResult = function(result){
            if(result.status === 'success'){
                return result;
            }
            else if(maxAttempts <= 0 || result.status === 'failure') {
                return Promise.reject(result);
            }
            else {
                maxAttempts -= 1;
                return (new Promise( function(resolve) {
                    setTimeout( function() {
                        resolve(_result);
                    }, 1000);
                })).then(function(){
                    return work.requestStatus().then(handleResult);
                });
            }
        };
        return work.requestStatus().then(handleResult);
    })
    .then(function(){console.log("work published"})
    .catch(console.error);
 Bergi06 июл. 2016 г., 03:01
и больше шансов сломаться, Это даже не проще, если вы привыкли к монадам.
 Hugo Silva06 июл. 2016 г., 03:50
Я не знаю ... Я не уверен в этом конкретном случае. Но для обучения я отредактировал код. Не могли бы вы взглянуть на это и дать мне знать ваши мысли? Обратите внимание, что я удалил один шаг из цепочки обещаний, что, по-моему, стало причиной путаницы.
 Bergi06 июл. 2016 г., 04:25
Выглядит лучше, спасибо. Вы скучаете поreturn доwork.requestStatus()… и один раньшеPromise.reject(result) (или ты долженthrow result), а ты имел ввидуmaxAttempts вместоcurrentAttempts
 Bergi06 июл. 2016 г., 01:50
Избегайте обещаний конструктора антипаттерна!
 Hugo Silva06 июл. 2016 г., 02:58
В этом случае, я думаю, я предпочитаю так называемый анти-шаблон, так как он приводит к тому, что код становится более читабельным и более легким для понимания. Спасибо за то, что указал на это, заставил меня получить некоторую ценную информацию.
 Hugo Silva06 июл. 2016 г., 02:45
@ Берги - я не могу понять, как решить эту проблему более читабельным способом. Не могли бы вы пролить свет? Также, пожалуйста, прокрутите вниз до этой статьи (github.com/petkaantonov/bluebird/wiki/...), где говорится оsetTimeoutи убедитесь, что этот сценарий не вписывается в исключение?
 Bergi06 июл. 2016 г., 02:49
Посмотрите на ответ друга :-)setTimeout подходит для исключения, поэтому используйтеPromise конструктор, чтобы получить обещание за задержку, ноrequestStatus возвращает обещание и использует егоPromise конструктор - это антипаттерн.

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

function delay(t) {
    return new Promise(function(resolve) {
        setTimeout(resolve, t);
    });
}

function checkStatus() {
    return work.requestStatus().then(function(result) {
        switch(result.status) {
            case "success":
                return result;      // resolve
            case "failure":
                throw result;       // reject
            case default:
            case "inProgress": //check every second
                return delay(1000).then(checkStatus);
        }
    });
}

work.create()
    .then(work.publish) //remote work submission
    .then(checkStatus)
    .then(function(){console.log("work published"})
    .catch(console.error);

Обратите внимание, я также избегал создавать обещания вокруг вашегоswitch заявление. Так как вы уже в.then() обработчик, просто возвращение значения является решением, создание исключения - отклонением, а возвращение обещания связывает новое обещание с предыдущим. Это охватывает три ветви вашегоswitch заявление без создания нового обещания там. Для удобства я используюdelay() функция, которая основана на обещании.

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

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

function delay(t) {
    return new Promise(function(resolve) {
        setTimeout(resolve, t);
    });
}

function checkStatus(timeout) {
    var start = Date.now();

    function check() {
        var now = Date.now();
        if (now - start > timeout) {
            return Promise.reject(new Error("checkStatus() timeout"));
        }
        return work.requestStatus().then(function(result) {
            switch(result.status) {
                case "success":
                    return result;      // resolve
                case "failure":
                    throw result;       // reject
                case default:
                case "inProgress": //check every second
                    return delay(1000).then(check);
            }
        });
    }
    return check;
}

work.create()
    .then(work.publish) //remote work submission
    .then(checkStatus(120 * 1000))
    .then(function(){console.log("work published"})
    .catch(console.error);

Я не уверен, какой именно «шаблон дизайна» вы ищете. Так как вы, кажется, возражаете против внешне заявленногоcheckStatus() функция, вот встроенная версия:

work.create()
    .then(work.publish) //remote work submission
    .then(work.requestStatus)
    .then(function() {
        // retry until done
        var timeout = 10 * 1000;
        var start = Date.now();

        function check() {
            var now = Date.now();
            if (now - start > timeout) {
                return Promise.reject(new Error("checkStatus() timeout"));
            }
            return work.requestStatus().then(function(result) {
                switch(result.status) {
                    case "success":
                        return result;      // resolve
                    case "failure":
                        throw result;       // reject
                    case default:
                    case "inProgress": //check every second
                        return delay(1000).then(check);
                }
            });
        }
        return check();
    }).then(function(){console.log("work published"})
    .catch(console.error);

Более повторяющаяся схема повторов, которая может использоваться во многих случаях, будет определять некоторый внешний код многократного использования, но вы, похоже, возражаете против этого, поэтому я не сделал эту версию.

Вот еще один подход, который использует.retryUntil() метод наPromise.prototype по вашему запросу. Если вы хотите настроить детали реализации этого, вы сможете изменить этот общий подход:

// fn returns a promise that must be fulfilled with an object
//    with a .status property that is "success" if done.  Any
//    other value for that status means to continue retrying
//  Rejecting the returned promise means to abort processing 
//        and propagate the rejection
// delay is the number of ms to delay before trying again
//     no delay before the first call to the callback
// tries is the max number of times to call the callback before rejecting
Promise.prototype.retryUntil = function(fn, delay, tries) {
    var numTries = 0;
    function check() {
        if (numTries >= tries) {
            throw new Error("retryUntil exceeded max tries");
        }
        ++numTries;
        return fn().then(function(result) {
            if (result.status === "success") {
                return result;          // resolve
            } else {
                return Promise.delay(delay).then(check);
            }
        });
    }
    return this.then(check);
}

if (!Promise.delay) {
    Promise.delay = function(t) {
        return new Promise(function(resolve) {
            setTimeout(resolve, t);
        });
    }
}


work.create()
    .then(work.publish) //remote work submission
    .retryUntil(function() {
        return work.requestStatus().then(function(result) {
            // make this promise reject for failure
            if (result.status === "failure") {
                throw result;
            }
            return result;
        })
    }, 2000, 10).then(function() {
        console.log("work published");
    }).catch(console.error);

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

work.create()
    .then(work.publish) //remote work submission
    .then(function() {
        var tries = 0, maxTries = 20;
        function next() {
            if (tries > maxTries) {
                throw new Error("Too many retries in work.requestStatus");
            }
            ++tries;
            return work.requestStatus().then(function(result) {
                switch(result.status) {
                    case "success":
                        return result;
                    case "failure":
                        // if it failed, make this promise reject
                        throw result;
                    default:
                        // for anything else, try again after short delay
                        // chain to the previous promise
                        return Promise.delay(2000).then(next);
                }

            });
        }
        return next();
    }).then(function(){
        console.log("work published")
    }).catch(console.error);
 user272719506 июл. 2016 г., 01:48
позвольте мне попробовать еще раз, вы знаете, как мы создали цепочки со всемиthen обработчики, использующие обещания, что если мы сможем связать обработчики повторов внутри без необходимости определения дополнительных функций, что-то в строках добавленияthenс внутренними повторами обещаний.
 user272719506 июл. 2016 г., 02:06
может это поможет,github.com/icodeforlove/promise-retryerвместо использования библиотеки, что-то определенное в Promise.prototype, которое будет принимать обратный вызов, задержку и максимальное количество повторов, а также разрешать, отклонять.
 jfriend0006 июл. 2016 г., 01:34
@ user2727195 - Предыдущая реализация не работала - поэтому я ее изменил. Это не зациклилось, пока результат не был найден. Это просто называетсяwork.requestStatus() еще один раз и не было возможности зацикливаться. Я считаю, что это соответствует требованиям вашего вопроса. Если вы ищете что-то помимо этого, пожалуйста, измените свой вопрос, чтобы указать, что еще вы ищете, и оставьте мне комментарий, чтобы сказать, что вы сделали это.
 jfriend0006 июл. 2016 г., 03:30
@ user2727195 - Так что ты думаешь о моем.retryUntil()? Вы были очень общительными, прежде чем я опубликовал это, и теперь вы вдруг молчите.
 Bergi06 июл. 2016 г., 01:52
@ user2727195: Вы можете тривиально переместитьcheckStatus функция вthen цепь, как объяснил выше друг. Он работает так же хорошо, как и выражение функции, как и объявление.
 user272719529 июл. 2016 г., 04:59
@ jfriend00 это очень помогло мне в уточнении того, что я искал.youtube.com/watch?v=lil4YCCXRYc сочетание шаблона AsyncIterator и Observer
 user272719506 июл. 2016 г., 01:50
может быть, до задержки функция в порядке, но тогда можно повторить обещания по цепочке?
 jfriend0006 июл. 2016 г., 01:58
@ user2727195 - Лично меня больше интересовал бы некоторый код многократного использования, который поможет вам повторить общее обещание. Так как ваша логика завершения является индивидуальной для этого случая, этот универсальный код должен был бы принять обратный вызов, который мог бы выполнить операцию и проверить ее результат и вернуть успех, сбой или продолжение работы. Но вы, кажется, не любите внешне определенные функции, поэтому я не пошел в этом направлении.
 user272719506 июл. 2016 г., 05:52
@ jfriend00, пожалуйста, посмотрите мой ответ, возможно, с кодом намерение очень ясно, теперь, если вы захотите, вы можете очистить свою доску для любых изменений в моем коде, который вы считаете подходящим, и я приму ваш ответ.
 user272719506 июл. 2016 г., 07:39
Я заинтересован в том, чтобы узнать о проблемах с безопасностью, пожалуйста, укажите исправление в моем коде № 2, я думаю, что любая ошибка вthen обработчики автоматически переносятся вreject обработчики
 user272719506 июл. 2016 г., 07:34
@ jfriend00 согласен, объединение существующих обещаний является лучшим, и они являются наиболее компактными решениями, не внося шума в код. Да, моя спецификация не была ясна, поскольку все было в моей голове, я должен был только сформулировать только то, что я хочу после того, как я продолжал повторять попытки.
 user272719506 июл. 2016 г., 01:51
например, Доминик написал это, но нет задержки.gist.github.com/domenic/2936696
 user272719506 июл. 2016 г., 01:59
да, это то, что я тоже ищу, повторение некоторых общих обещаний, возможно, определите один из Promise.prototype,
 jfriend0008 июл. 2016 г., 18:14
@ user2727195 - Я добавил еще одну опцию, которая использует встроенный код. У меня нет других идей, поэтому, пожалуйста, опишите, чего не хватает в этом ответе, или каким-то образом завершите вопрос.
 jfriend0006 июл. 2016 г., 01:48
@ user2727195 - Повторно используемый шаблон повторов, который во многих случаях прост для повторного использования, потребует внешней функции, реализующей общий код, который вы собираетесь использовать повторно. Это разрешено? Ваши требования слишком расплывчаты, чтобы я знал, что вы ищете.
 jfriend0006 июл. 2016 г., 01:54
@ user2727195 - я добавил встроенную версию.
 jfriend0006 июл. 2016 г., 02:01
@ user2727195 - Как я уже спрашивал несколько раз, отредактируйте свой вопрос, чтобы описать, что вы хотите. Очень неприятно ответить на вопрос, который, по вашему мнению, был задан, а затем сказать, что это не совсем то, что вы хотели задать, а затем найти неясные и развивающиеся требования. Это движущаяся цель, очень трудно ударить ответом. Когда мне кажется, что я понимаю, что вы хотите от слов вашего вопроса (а не только от накопления ваших комментариев), я могу сделать еще один снимок более общего решения. Но я буду ждать до тех пор.
 jfriend0006 июл. 2016 г., 01:46
@ user2727195 - Это, вероятно, можно сделать более общим. Пожалуйста, измените свой вопрос, чтобы точно описать, какие критерии вы ищете. Вы смутно упоминаете «шаблон проектирования», но не очень точно описываете, что действительно соответствует вашим требованиям.
 jfriend0006 июл. 2016 г., 06:32
@ user2727195 - У вас есть несколько идей, как это сделать. У меня есть несколько идей о том, как это сделать. Вы не предложили спецификацию именно для того, что хотели, так что это всего лишь идеи, которые могли бы работать. Мне нравится объединять существующие обещания в качестве общего шаблона проектирования обещаний, а не создавать новое обещание упаковки по ряду причин, связанных с тем, как работают обещания, и вероятностью того, что кто-то может пострадать от ошибок программирования. Например, в каждом из ваших примеров в некоторых местах отсутствует безопасность броска.
 user272719506 июл. 2016 г., 08:29
@ jfriend00 Я думаю, что сформулированный вопрос заключается в том, как я могу продолжать повторную попытку до тех пор, покаthen результат многократного использования (условие - это то, что будет меняться) при использовании существующих цепочек обещаний, а не при создании новых. Я подумаю над этим, и вы тоже можете, если захотите, если вы думаете, что сможете тренироваться в течение нескольких дней, дайте мне знать, или я могу закрыть это с принятием, чтобы ответить здесь.
 user272719506 июл. 2016 г., 01:44
дело в том, чтобы иметь шаблон многократного использования, я имею в виду, что мне нужно отменить публикацию, и критерии разные, и то же самое с удалением. Многоразовый шаблон дизайна, если мы можем разработать. В предыдущей реализации мне нравилось, как вы объединяли обещания (если я правильно помню), а затем, если все прошло успешно, все решилось обратно. в противном случае, если я продолжу писать дополнительный пользовательский код, он станет трудоемким. Также отредактировал мой вопрос, надеюсь, он прояснится
 jfriend0006 июл. 2016 г., 01:38
@ user2727195 - ThecheckStatus() реализация может быть сделана встроенной без отдельно названной функции, если это вас как-то беспокоит, но я подумал, что это была более чистая реализация, как и я, потому что она делает.then().then().then() цепочка намного проще, чтобы следить и точно видеть, что там происходит. Кроме того, мне нужно было создать замыкание для управления тайм-аутом, и это был удобный способ сделать это без введения каких-либо переменных на верхнем уровне области действия.
 jfriend0006 июл. 2016 г., 01:56
@ user2727195 - Вы не можете бесконечно асинхронно повторять код, не имея функции, которую можно вызывать повторно. Вот почемуcheck() определяется как функция. Это то, как вы делаете тело кода, которое вы можете неоднократно вызывать асинхронно.
 user272719506 июл. 2016 г., 07:35
И угадайте, что я придерживаюсьsetInterval (Код № 2) подход для обоих. В основном я работаю с «повторное обещание до разрешения» и «повторное обещание с условием на результат» в пределахsetInterval блок внутриthen обработчик, а затем прогрессирует только после разрешения или отклонения после очистки интервалов. Это компактная модель была бы для меня, не закатывая глаза еще где. Я надеюсь, вы понимаете, о чем я. Я бы принял вашу попытку. Ценю твою помощь. Я опускаю код № 1 для любых дальнейших попыток и продолжаю использовать код № 2 всякий раз, когда мне нужно повторение.
 jfriend0006 июл. 2016 г., 02:19
@ user2727195 - я добавил одну последнюю реализацию с использованием универсального.retryUntil() на прототипе Promise. Проблема с обратным вызовом, которыйretryUntil() Считается, что он возвращает обещание, которое должно сообщать о трех состояниях (сделано, еще не сделано, ошибка). Я решил разрешить его с объектом, который имеет.status собственности, хотя есть много способов сделать это. Способ, который я выбрал, позволяет вам разрешить множество других данных, кроме.status стоимость, которая, казалось бы, требует в некоторых обстоятельствах, но это может быть структурировано по-другому.
 user272719506 июл. 2016 г., 01:24
Мне понравилась предыдущая реализация, я тоже решил ее как-то, но она мне не понравилась, и так же, как и в вашем случае, мне понравилась ваша предыдущая реализация, которую вы удалили, и она мне понравилась, потому что это встроенная реализация в тогдашнем обработчике, цель - чтобы придумать элегантный шаблон проектирования, который затем работает с обработчиком, без необходимости использования сторонних функций, а не просто решить проблему, пожалуйста, переделайте

Упоминается много хороших решений, и теперь с помощью async / await эти проблемы могут быть решены без особых усилий.

Если вы не возражаете против рекурсивного подхода, то это мое решение.

function retry(fn, retries=3, err=null) {
  if (!retries) {
    return Promise.reject(err);
  }
  return fn().catch(err => {
      return retry(fn, (retries - 1), err);
    });
}
 holmberd27 нояб. 2018 г., 16:09
@DavidLemon да, скорее всего, у вас будетcatch() на вашеretry() чтобы предоставить вам последнюю ошибку повторных попыток и информацию о том, что все повторные попытки завершились неудачно.
 David Lemon27 нояб. 2018 г., 10:21
Мне нравится этот простой подход, который выдерживает испытание временем. Я бы только добавил обработчик ошибок.

Одна библиотека может сделать это легко:Обещание-повторы.

Вот несколько примеров, чтобы проверить это:

const promiseRetry = require('promise-retry');

Ожидайте вторую попытку быть успешной:

it('should retry one time after error', (done) => {
    const options = {
        minTimeout: 10,
        maxTimeout: 100
    };
    promiseRetry((retry, number) => {
        console.log('test2 attempt number', number);
        return new Promise((resolve, reject) => {
            if (number === 1) throw new Error('first attempt fails');
            else resolve('second attempt success');
        }).catch(retry);
    }, options).then(res => {
        expect(res).toBe('second attempt success');
        done();
    }).catch(err => {
        fail(err);
    });
});

Ожидайте только одну попытку:

it('should not retry a second time', (done) => {
    const options = {
        retries: 1,
        minTimeout: 10,
        maxTimeout: 100
    };
    promiseRetry((retry, number) => {
        console.log('test4 attempt number', number);
        return new Promise((resolve, reject) => {
            if (number <= 2) throw new Error('attempt ' + number + ' fails');
            else resolve('third attempt success');
        }).catch(retry);
    }, options).then(res => {
        fail('Should never success');
    }).catch(err => {
        expect(err.toString()).toBe('Error: attempt 2 fails');
        done();
    });
});
Решение Вопроса

Что-то немного другое ...

Асинхронные повторы могут быть достигнуты путем создания.catch() цепь, в отличие от более обычной.then() цепь.

Этот подход:

возможно только с указанным максимальным количеством попыток. (Цепь должна быть конечной длины),рекомендуется только с низким максимумом. (Цепочки обещаний потребляют память, примерно пропорциональную их длине).

В противном случае используйте рекурсивное решение.

Во-первых, функция полезности, которая будет использоваться в качестве.catch() Перезвоните.

var t = 500;

function rejectDelay(reason) {
    return new Promise(function(resolve, reject) {
        setTimeout(reject.bind(null, reason), t); 
    });
}

Теперь вы можете создавать цепочки .catch очень кратко:

1. Повторите попытку, пока обещание не будет выполнено, с задержкой.

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).catch(rejectDelay);
}
p = p.then(processResult).catch(errorHandler);

DEMO: https://jsfiddle.net/duL0qjqe/

2. Повторите попытку, пока результат не удовлетворяет некоторому условию, без задержки.

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).then(test);
}
p = p.then(processResult).catch(errorHandler);

DEMO: https://jsfiddle.net/duL0qjqe/1/

3. Повторите попытку, пока результат не удовлетворяет некоторому условию, с задержкой

Обдумав (1) и (2), комбинированный тест + задержка одинаково тривиален.

var max = 5;
var p = Promise.reject();

for(var i=0; i<max; i++) {
    p = p.catch(attempt).then(test).catch(rejectDelay);
    // Don't be tempted to simplify this to `p.catch(attempt).then(test, rejectDelay)`. Test failures would not be caught.
}
p = p.then(processResult).catch(errorHandler);

test() может быть синхронным или асинхронным.

Также было бы тривиально добавить дополнительные тесты. Просто вставьте цепочку между двумя защелками.

p = p.catch(attempt).then(test1).then(test2).then(test3).catch(rejectDelay);

DEMO: https://jsfiddle.net/duL0qjqe/3/

Все версии предназначены дляattempt быть возвращающей обещание асинхронной функцией. Также возможно, что он может вернуть значение, и в этом случае цепочка будет следовать по пути успеха к следующему / терминалу..then().

 user272719506 июл. 2016 г., 23:00
Кстати, отличная работа открыла еще одно измерение об обещаниях
 Roamer-188806 июл. 2016 г., 23:38
Да, забудь оsetInterval(), Ему не место в решении вашего вопроса.
 user272719506 июл. 2016 г., 23:22
NOPS,setInterval это не решение в моих попытках
 user272719506 июл. 2016 г., 22:28
Promise chains consume memory roughly proportional to their length но они собираются быть освобождены в конце урегулирования?
 user272719506 июл. 2016 г., 22:56
с вашим решением № 2 и № 3 есть кое-чтоp.catch(attempt).then(test).catch(rejectDelay);, в основном, если попытка разрешается где-то посередине, все следующиеthen(test) Я думаю, решение работает для меня, но если у вас есть что-то по принципу «нужно объединить / динамически», не усложняя все и не повредив существующие решения, это было бы замечательно.
 user272719506 июл. 2016 г., 23:09
для № 3, я принял ваш подход и заменитьfor цикл сsetInterval и очисткаintervalId на разрешении / отклонении, поэтому я считаю, что это будет продолжаться, только если есть необходимость, не усложняя все. Для № 1 и № 2 я должен добавить дополнительное условие, чтобы проверитьmax пытается иclearInterval затем.
 jfriend0008 июл. 2016 г., 19:52
@ user2727195 - я тоже не уверен, какtest() функция может сообщать разницу между ошибкой с повторением повторения или неудачей с ошибкой, которая должна быть сообщена обратно, и все попытки отменены. Поскольку многие решения имеют такую ​​структуруp.catch(attempt).catch(rejectDelay);нет возможности отклонить вattempt() прекратить дальнейшую обработку. Это кажется слишком упрощенным для реальной ситуации в мире. Обычно есть сбои, которые указывают на то, что повторная попытка желательна, и сбои, которые указывают на то, что дальнейшие попытки не должны выполняться, и фактически это, кажется, имеет место в коде OP.
 Roamer-188807 июл. 2016 г., 00:03
"Возможно ли динамическое сцепление, основанное на необходимости?" - нет, этот подход полностью основан на построении цепочки, а затем позволяет обосноваться.
 Roamer-188807 июл. 2016 г., 00:05
Решения № 2 и № 3 действительно могут объединить ряд.then(test) шаги вниз по течению от первого.then(test) который видит успешно возвращенные данные. Письменноtest() как в демках, т.е.return val при прохождении теста, все ниже по течению.then(test) шаги (при условии, что тест является идемпотентным) приведут к повторному прохождению того же теста. Тщательно логично, но ужасно неэффективно, особенно если тесты асинхронные. Я уверен, что можно было бы придумать механизм, чтобы избежать повторного применения одного и того же теста снова и снова, но этот подход потерял бы свою простоту.
 user272719506 июл. 2016 г., 23:50
согласен, возможно ли динамическое связывание в зависимости от потребности?
 user272719506 июл. 2016 г., 22:26
Когда ты сказал,only possible with a specified maximum number of attemptsЯ могу вернуться кsetInterval основанный метод для неограниченного и сделать объединение обещаний в рамкахsetInterval Метод, было бы неплохо увидеть ваш пример для неограниченных попыток, если вы не возражаете. он будет использоваться, когда я уверен в результатеresolve или жеreject но эта процедура (публикация) иногда занимает слишком много времени в зависимости от размера файла.
 user272719506 июл. 2016 г., 22:32
Я отредактировал № 3 как неограниченное количество повторных попыток, и я ожидаю некоторой эффективной динамической конкатенации / цепочки памяти.
 jfriend0008 июл. 2016 г., 19:47
Весь этот путь решения кажется мне странным. Поскольку он может повторить до N раз, он предварительно создает N объектов на случай, если они могут понадобиться. Если фактическая операция завершается успешно с первой попытки, то вы не только создали ненужные объекты N-1, но затем их необходимо было утилизировать. Это просто кажется концептуально неэффективным, а иногда и практически неэффективным, если N не является небольшим числом. Он также не может принимать произвольные решения о том, сколько раз повторить попытку, как рекурсивно связанное решение. Например, он не знает, как реализовать «повторить попытку автоматически на срок до 2 минут».

2. Шаблон, который продолжает повторяться до тех пор, пока условие не будет соответствовать результату. (с задержкой и maxRetries)

Это хороший способ сделать это с родными обещаниями рекурсивным способом:

const wait = ms => new Promise(r => setTimeout(r, ms));

const retryOperation = (operation, delay, times) => new Promise((resolve, reject) => {
  return operation()
    .then(resolve)
    .catch((reason) => {
      if (times - 1 > 0) {
        return wait(delay)
          .then(retryOperation.bind(null, operation, delay, times - 1))
          .then(resolve)
          .catch(reject);
      }
      return reject(reason);
    });
});

Вот как вы это называете, предполагая, чтоfunc иногда успешно, а иногда и не всегда, возвращая строку, которую мы можем записать:

retryOperation(func, 1000, 5)
  .then(console.log)
  .catch(console.log);

Здесь мы вызываем retryOperation и просим его повторять каждую секунду, а max retries = 5.

Если вы хотите что-то более простое без обещаний, RxJs поможет с этим:https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/retrywhen.md

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