изчакване за завършване на ajax преди следващото повикване на ajax

Опитвам се да изчакам извикване на ajax, за да завърши записването на модел, преди да запиша следващия модел в списъка. Търсих в гугъл и видях някои неща за отложени обекти, които са нови за мен, и друг отговор, който имаше рекурсивна функция, го направи. Опитах рекурсивния метод, защото изглеждаше, че има малко повече смисъл, отколкото с отложени обекти и използване на $.when.apply($, arrayOfAjaxCalls).then(). Така че този код (рекурсивният, изглежда така:

    saveModel(index, numRequests) {
        var self = this;

        if (index < numRequests) {
            var sample = self.samplesToSave[index];    

            return $.ajax({
                url: model.url,
                contentType: "application/json",
                type: "POST",
                data: JSON.stringify(model),
                crossDomain: $.support.cors,
                xhrFields: {
                    withCredentials: $.support.cors,
                },
                success: function(data) {
                    console.log("JUST SAVED");
                    console.log(data);
                },
                error: function(xhr: any) {
                    console.log(xhr);
                },
            }).then(() => {
                self.saveModel(index + 1, numRequests);
            });
        }
    }

Наричам това така:

saveModel(0, _.size(myCollection)

Всъщност не изчаква извикването на ajax да завърши в текущото си състояние, преди да извика следващия saveModel. По същество просто синхронно извиква saveModel за всеки елемент в колекцията по ред. Някакви мисли какво пропускам? Ако има по-добро решение с $.Deferred, аз също съм съгласен с това. Благодаря.

Редактиране: Съжалявам, че исках да кажа saveModel в последния ред на функцията saveModel. Опитваше се да се отърве от части, които бяха специфични за домейна. И използвам typescript, а не coffeescript

Нов опит:

    saveSampleNew() {
        var d = $.Deferred();
        d.resolve();
        var p = d.promise();
        var self = this;
        self.samplesToSave.forEach(sample => p = p.then(() => self.makeSaveRequest(sample)));

        return p;
    }

     makeSaveRequest(sample) {
        var self = this;

        return $.ajax({
            url:  "samples",
            contentType: "application/json",
            type: "POST",
            data: JSON.stringify(sample),
            crossDomain: $.support.cors,
            xhrFields: {
                withCredentials: $.support.cors,
            },
            success: function(data) {
                console.log("SAVED12");
                console.log(data);                    
            },
        });
    }

Тъй като този код зависи от завършването на други асинхронни извиквания, наричам този нов опит така:

this.saveContainers(project).then(() => {

}).done(() => {
    self.saveSampleNew();
});

person Crystal    schedule 17.05.2014    source източник
comment
Това coffescript ли е? Ако да, моля, маркирайте въпроса си с него.   -  person Bergi    schedule 17.05.2014
comment
Къде изобщо наричате следващия saveModel? Единственото нещо, което виждам е self.modelDeferred(…). Моля, предоставете пълен пример.   -  person Bergi    schedule 17.05.2014
comment
@Bergi: Прилича повече на ES6 код.   -  person Felix Kling    schedule 17.05.2014
comment
Кодът изглежда трябва да работи според очакванията. Ако не сте запознати с обещанията/отложените обекти, погледнете learn.jquery.com/code -organization/deferreds и html5rocks.com/en/tutorials/es6/ обещания.   -  person Felix Kling    schedule 18.05.2014
comment
@FelixKling: Затова попитах. Afaik, от ES6 стрелкови функции с тяло на блок ще трябва изрично да return. Разбира се, няма много смисъл да използвате self вместо this във функция със стрелка, независимо от езика.   -  person Bergi    schedule 18.05.2014
comment
@Bergi: Правилно. Но трябва да използвате само return, ако искате да върнете нещо ;) Първоначалната ми мисъл беше, че нещо трябва да се върне, но не знам как се нарича кодът като цяло, така че... не съм сигурен :-/   -  person Felix Kling    schedule 18.05.2014
comment
Да, обикновено винаги искате да return от .then() обещание за обратно извикване. В този случай ще трябва да върне рекурсивното обещание, в противен случай обещанието за връщане на първоначалното извикване би се разрешило твърде рано с тази undefined стойност. Тъй като не виждаме целия код, аз също не съм сигурен...   -  person Bergi    schedule 18.05.2014
comment
@Bergi: Да, ако искате да направите нещо, след като всички обаждания са разрешени, тогава ще ви трябва. Но дори и без него Ajax извикванията трябва да се изпълняват последователно. Е... може би OP се интересува достатъчно, за да изясни някои неща в даден момент;)   -  person Felix Kling    schedule 18.05.2014


Отговори (2)


Не, трябва да работи по този начин. Ако смятате, че не чака, моля, предоставете повече информация за това как го извиквате и какъв е опитът, че то прави синхронна рекурсия.

Има обаче една уловка с рекурсивното извикване:

.then(function() {
  self.saveModel(index + 1, numRequests);
})

Обещанието, което се връща от then и впоследствие от вашия saveModel метод, се разрешава директно с първото извикване на ajax, то не чака рекурсивната верига. Другите ajax извиквания все още се извършват (последователно, както се очаква), но не се проследяват от полученото обещание.

За да получите това и правилно да верижирате обещанията, така че да се разреши с резултата от последното („най-вътрешно“) обещание, ще трябва да return обещанието от обратното извикване към then:

.then(function() {
  return self.saveModel(index + 1, numRequests);
})
person Bergi    schedule 18.05.2014
comment
Опитах това и изглежда не работи. Той извиква saveModel в ред, както преди, но всъщност не изчаква saveModel() да завърши първия път, преди да го извика втори път. - person Crystal; 19.05.2014

Вероятно бих използвал for цикъл, а не рекурсивно извикване тук, като цяло - намирам ги за по-лесни за четене в този контекст.

saveModel() {
     var d = $.Deferred(); d.resolve();
     var p = d.promise(); // to start the chain
     this.samplesToSave.forEach(sample => p = p.then(() => makeSaveRequest(sample));
     return p;
}

makeSaveRequest(sample) {
    return $.ajax({...}); // make request using `sample` as data
} 
person Benjamin Gruenbaum    schedule 18.05.2014
comment
Опитах това, но изглежда не постига ефекта от изчакването на първия makeSaveRequest да завърши, преди да го извикам отново за следващия обект. - person Crystal; 19.05.2014
comment
@Crystal, тогава може да го използвате погрешно, можете ли да ми покажете пример? Сигурни ли сте, че makeSaveRequest прави return $.ajax(...? - person Benjamin Gruenbaum; 19.05.2014
comment
Добре, актуализирах оригиналната си публикация в долната част. Можете ли да погледнете, моля? Благодаря. - person Crystal; 19.05.2014
comment
Добре, добавих как го наричам. Уведомете ме, ако имате нужда и от всички други методи. Реших, че това е повече логика на домейна и може да бъде пропуснато. - person Crystal; 19.05.2014
comment
@Crystal не го чакаш никъде, как се закачаш да се прави? Трябва да направите this.saveContainers(project).then(() => self.saveSampleNew()).done(function(p){ console.log("Done saving all values!"); }); - person Benjamin Gruenbaum; 19.05.2014
comment
Така че виждам как Done saving all values ​​ще бъде извикано, когато saveSampleNew() приключи, но търся отделните извиквания на makeSaveRequest да изчакат, преди да го извикам отново. Така че, когато makeSaveRequest приключи с първия обект, той ще го извика отново за втория обект и така нататък. В момента те се извикват по ред, но не изчаква един да приключи, преди да извика следващия - person Crystal; 19.05.2014
comment
@Crystal сигурен ли си? Ще постави на опашка всички наведнъж, но ще ги изпълни един след друг. - person Benjamin Gruenbaum; 19.05.2014
comment
Струва ми се така, тъй като обектите ми от база данни се записват в ред понякога, но не винаги. Също така имам дневник, който правя в манипулатора на успеха и мога да видя в конзолата, че може да получа две публикации преди оператора за дневник. - person Crystal; 19.05.2014