Функция $.Deferred.then() не позволяет неизвестному количеству раз динамически вызывать функцию?

Я наткнулся на этот скрипт из поиска Google по адресу jQuery. Deferred и Promise для последовательного выполнения синхронных и асинхронных функций .

Это может решить мою проблему с выполнением скрипта в последовательном порядке. Но проблема с функцией then() заключается в том, что я не могу узнать, сколько раз я могу вызывать эту функцию. Я могу вызвать функцию a() 3 раза, в следующий раз я могу вызвать ее 20 раз и т. д. Так что с then() это не сработает. Функция then() не допускает стек массива. Итак, что вы, люди, делаете, чтобы заставить эту концепцию работать?

fucntion a(counter)
{
    $deferred = $.Deferred();

    //Run some scripts here...
    alert(counter);

    $deferred.resolve();
    return $deferred;
}
function Test()
{
    var d = $.Deferred(), 
    p=d.promise();
    p.then(a(0)).then(a(1)).then(a(2));
    d.resolve();
    return d;
}

Test().done(function(){ alert('done'); }).fail(function() { alert('fail'); });

person fletchsod    schedule 03.09.2014    source источник
comment
Вы передаете $deferred, возвращенное a(N), в .then(). Вместо этого вам нужно будет передать функцию обратного вызова, например. .then(a).   -  person Bergi    schedule 03.09.2014
comment
Я не уверен, что вы имеете в виду, когда говорите о многократном вызове Test()? Что в этом плохого, что происходит, когда вы делаете то, чего не ожидали? Что вы подразумеваете под разрешить стек массива?   -  person Bergi    schedule 03.09.2014
comment
Я обновил вопрос, я имею в виду a(), а не Test()...   -  person fletchsod    schedule 03.09.2014
comment
Да, вы вызываете a(0); a(1); a(2); одновременно, что заставляет асинхронные процессы выполняться одновременно.   -  person Bergi    schedule 03.09.2014
comment
Да. Сколько раз ее вызывать случайно, в первый раз это может быть вызов функции a() 3 раза. Позже в течение дня он может вызывать функцию a() 20 раз. Сколько раз звонить это делается случайным образом. $.Deferred.then() не выполняет случайное число раз для вызова функции a().   -  person fletchsod    schedule 03.09.2014
comment
Так в чем вопрос? Какую концепцию вы пытаетесь заставить работать?   -  person Matt Burland    schedule 03.09.2014
comment
Извините за мой плохой английский. Мой вопрос заключается в том, как динамически добавлять then(), не зная, сколько раз вызывается функция a().   -  person fletchsod    schedule 03.09.2014


Ответы (2)


Я думаю, что вы спрашиваете, как связать и неизвестное количество вызовов. В этом случае вы можете легко зациклиться и просто продолжать добавлять больше .then. Например:

for (var i=0; i < someLimit; i++) {
    p = p.then(a(i));
}

Это свяжет вызовы, чтобы они вызывались один за другим. Если вы хотите, чтобы они выполнялись параллельно, то предложение @jantimon по использованию .when - это то, что вам нужно.

Это работает из-за того, как работает цепочка. Вызов .then возвращает обещание, так что вы можете снова вызвать .then для возвращенного обещания. См.: http://api.jquery.com/deferred.then/.

person Matt Burland    schedule 03.09.2014
comment
Спасибо. Да, цепочка с неизвестным количеством вызовов. Я не знал, что это сработает. - person fletchsod; 03.09.2014
comment
Мне нравится ваше решение, но оно также будет выполнять их параллельно, не так ли? - person jantimon; 03.09.2014
comment
@jantimon: № .then будет выполнен, когда будет разрешено отложенное. Поэтому они будут выполняться один за другим. Не путать с .when - person Matt Burland; 03.09.2014
comment
@MattBurland: a(i) возвращает не функцию, а обещание. Он будет выполнять их параллельно, ничего не дожидаясь… - person Bergi; 03.09.2014
comment
@Bergi: Ах, да, я пропустил эту часть. Но я не уверен, почему они вообще возвращают deferred. - person Matt Burland; 03.09.2014
comment
@Bergi Хорошо, ты говоришь, что есть лучший или более простой способ? Если да, то можете ли вы показать мне один? Я все еще учусь, пытаясь избежать старомодных способов обратного вызова. - person fletchsod; 03.09.2014
comment
@fletchsod: вы не можете избежать передачи обратного вызова на then, преимущество промисов в том, что вы можете их разложить. Взгляните на это или это - person Bergi; 03.09.2014

Вы можете использовать jQuery.when.
Обратите внимание, что это приведет к одновременному выполнению нескольких асинхронных операций:

демонстрация jsFiddle

Учитывая вам функцию a, которая возвращает асинхронное обещание:

function a(counter, $target) {
    var $deferred = $.Deferred();
    setTimeout(function () {
        $target.text($target.text() + ' ' + counter);
        $deferred.resolve();
    }, 1000);
    return $deferred;
}

В параллели:

function Test1() {
    return $.when($.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function (val, i) {
        return a(i, $('#test1'));
    }));
}

Test1().done(function () {
    $('#test1').css('font-weight', 'bold');
}).fail(function () {
    alert('fail');
});

Чтобы запустить его последовательно, вам понадобится помощник promiseLoop:

function promiseLoop(arr, callback) {
    return arr.reduce(function (previousPromise, val, i) {
        return previousPromise.then(function (resultArray) {
            return $.when(callback(val, i)).then(function (res) {
                return resultArray.concat([res]);
            });
        });
    }, $.when([]));
}

function Test2() {
    return promiseLoop([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function (val, i) {
        return a(i, $('#test2'));
    });
}


Test2().done(function () {
    $('#test2').css('font-weight', 'bold');
}).fail(function () {
    alert('fail');
});
person jantimon    schedule 03.09.2014
comment
Спасибо. Я сохраню копию этого скрипта, если мне нужно запустить его в параллельном потоке. - person fletchsod; 03.09.2014
comment
См. демонстрацию для примера, который запускает его параллельно или последовательно. - person jantimon; 03.09.2014
comment
Хммм... Мило... Спасибо. :-) - person fletchsod; 03.09.2014