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

Попаднах на този скрипт от търсенето с Google в jQuery Отложено и обещание за последователно изпълнение на синхронни и асинхронни функции .

Това може да реши моя проблем при изпълнението на скрипта в последователен ред. Но проблемът, който имам с функцията 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: А, да, пропуснах тази част. Но не съм сигурен защо изобщо връщат отложено. - 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