Как прервать последовательность асинхронных операций в цикле?

Следуя этому примеру

Часто задаваемые вопросы о Dojo: Как упорядочить асинхронные операции?

function doNext(previousValue) {
    var dfd = new Deferred();

    // perform some async logic; resolve the promise
    setTimeout(function () {
        var next = String.fromCharCode(previousValue.charCodeAt(previousValue.length - 1) + 1);
        dfd.resolve(previousValue + next);
    }, 50);

    return dfd.promise;
}

var promise = doNext('a');

for (var i = 0; i < 9; i++) {
    promise = promise.then(doNext);
}

promise.then(function (finalResult) {
    // 'doNext' will have been invoked 10 times, each
    // invocation only occurring after the previous one completed

    // 'finalResult' will be the value returned
    // by the last invocation of 'doNext': 'abcdefghijk'
    console.log(finalResult);
});

Как мне выйти из цикла - т.е. прекратить обработку последующих вызовов функции doNext, когда doNext соответствует определенным критериям - например, остановить, когда следующим символом является 'd', и вернуть вычисленное значение до этой точки?

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

setTimeout(function () {
    var next = String.fromCharCode(previousValue.charCodeAt(previousValue.length - 1) + 1);
    if(previousValue + next == 'abc')
        dfd.cancel('abc');
    else
    dfd.resolve(previousValue + next);
}, 50);

person erotavlas    schedule 16.03.2017    source источник


Ответы (2)


Вы можете сделать это, проверив значение, возвращаемое из обещания, и решить, вызывать ли асинхронный запрос снова или нет. Это не похоже на добавление break в цикл for. Но результат будет таким, каким вы желаете.

Все 9 promise.then будут вызваны, но doNext не будет вызван 9 раз. Ниже приведен фрагмент того же.

for (var i = 0; i < 9; i++) {
    promise = promise.then(function(val){
        return val === "abcd" ? val : doNext(val);
    });
}

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

https://jsbin.com/qiwesecufi/edit?js,console,output

Другое место, где вы можете выполнить проверку, находится внутри самой функции doNext.

function doNext(previousValue) {
    var dfd = new Deferred();

    if(previousValue === "abcd")
        return previousValue;

    // perform some async logic; resolve the promise
    setTimeout(function () {
        var next = String.fromCharCode(previousValue.charCodeAt(previousValue.length - 1) + 1);
        dfd.resolve(previousValue + next);
    }, 1000);

    return dfd.promise;
}

Надеюсь, это было полезно.

person T Kambi    schedule 16.03.2017

Вы должны использовать подход цикла reduce (или promise = promise.then(doNext)) только тогда, когда вы всегда хотите обработать все элементы (или синхронно решить, сколько вам нужно).

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

function wait(t, v) {
    var dfd = new Deferred();
    // asynchronously resolve the promise
    setTimeout(function () {
        dfd.resolve(v);
    }, t);
    return dfd.promise;
}

function doNext(previousValue) {
    var next = String.fromCharCode(previousValue.charCodeAt(previousValue.length - 1) + 1);
    return wait(50, previousValue + next);
}

function loop(v, i) {
    if (i <= 0) return when(v);
    if (v == "abc") return when("abc");
    return doNext(v).then(function(r) {
        return loop(r, i-1);
    });
}

loop('a', 9).then(function (finalResult) {
    console.log(finalResult);
});
person Bergi    schedule 16.03.2017
comment
Я пробовал ваш пример, но два вопроса 1) - что такое «когда»? 2) при пошаговом выполнении кода он никогда не выводит окончательный результат на консоль, так ли это? - person erotavlas; 16.03.2017
comment
when, похоже, является способом написания _ 2_. Что касается окончательного результата, да, определенно должен, хотя я не тестировал код (и только заметил, что забыл пройти i). - person Bergi; 16.03.2017