ожидание завершения 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. Пытался избавиться от частей, которые были специфичны для домена. И я использую машинопись, а не кофескрипт

Новая попытка:

    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
Где вы вообще вызываете следующую модель сохранения? Единственное, что я вижу, это self.modelDeferred(…). Пожалуйста, приведите полный пример.   -  person Bergi    schedule 17.05.2014
comment
@Bergi: больше похоже на код ES6.   -  person Felix Kling    schedule 17.05.2014
comment
Код выглядит так, как будто он должен работать так, как ожидалось. Если вы новичок в объектах promises/deferred, загляните на 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
@ Берги: Правильно. Но вам нужно использовать return только в том случае, если вы хотите что-то вернуть ;) Моя первоначальная мысль заключалась в том, что что-то должно быть возвращено, но я не знаю, как код называется в целом, поэтому... не уверен :-/   -  person Felix Kling    schedule 18.05.2014
comment
Да, обычно вы всегда хотите return из обратного вызова обещания .then(). В этом случае ему нужно будет вернуть рекурсивное обещание, иначе обещание возврата исходного вызова разрешится слишком рано с этим значением undefined. Поскольку мы не видим весь код, я тоже не уверен…   -  person Bergi    schedule 18.05.2014
comment
@Bergi: Да, если вы хотите что-то сделать после того, как все звонки будут разрешены, вам это понадобится. Но и без него вызовы Ajax должны выполняться последовательно. Ну ... может быть, ОП достаточно заботится, чтобы в какой-то момент прояснить некоторые вещи;)   -  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, сохраняющее все значения, будет вызвано, когда saveSampleNew() завершится, но я ищу отдельные вызовы makeSaveRequest для ожидания, прежде чем вызывать его снова. Поэтому, когда makeSaveRequest закончит работу с первым объектом, он снова вызовет его для второго объекта и так далее. Прямо сейчас они вызываются по порядку, но не ждут, пока закончится один, прежде чем вызывать следующий. - person Crystal; 19.05.2014
comment
@Кристалл, ты уверен? Он поставит в очередь их все сразу, но выполнит их один за другим. - person Benjamin Gruenbaum; 19.05.2014
comment
Мне так кажется, поскольку объекты моей базы данных иногда сохраняются по порядку, но не всегда. Также у меня есть журнал, который я делаю в обработчике успеха, и я вижу в консоли, что могу получить два сообщения перед оператором журнала. - person Crystal; 19.05.2014