Путаница между jQuery Deferrable, jsDeferred и просто deferred в целом

#javascript #jquery #jquery-deferred

#javascript #jquery #jquery-отложенный

Вопрос:

Я скачал библиотеку, призванную jsdeferred попытаться помочь мне с некоторыми проблемами с потоком кода, но я немного запутался, так как ее примеры и … «документация» немного неясны в некоторых вещах. Но по мере того, как я продолжал читать и копать, и, конечно, гуглил все под солнцем, я также обнаружил, что у jQuery есть своя собственная Deferred() система. Я связываю оба здесь для правильного контекста.

Ссылка на библиотеку jsDeferred

Ссылка на jQuery.Отложенный ()

Проблема

Мне нужно найти способ указать странице «держаться, пока не будет сделано последнее».

Это то, что сделал thought jsdeffered . Итак, часть моего вопроса заключается в том, что я должен использовать? jsDeferred или jQuery.Deferred(); а затем, как его использовать, как я изложил ниже.

Ситуация

Мой сценарий таков, в двух словах, мне нужно выполнить следующее поведение.

  • страница загружается, view model определяется

Это используется kendo ui mvvm для объявления моей модели представления, поэтому это kendo.data.ObservableObject

  • для $.ajax получения некоторых данных модели по умолчанию выполняется вызов базы данных

Вот где у меня больше всего проблем. Мне нужно, чтобы все «держалось», пока это $.ajax не будет сделано. Но я не хочу оборачивать все в $.ajax().done(r) , если я могу с этим поделать. Для меня это выглядит / кажется очень неаккуратным и иногда сбивает с толку.

  • другие виджеты на странице отображаются, они выполняют соответствующие запросы к базе kendo ui Remote DataSource данных.

На самом деле они работают так, как задумано.

  • jQuery Validate подключен к представлению, при этом значения по умолчанию уже установлены.

Это также работает по назначению.

  • kendo.bind('body', viewModel); вызывается для выполнения привязки модели.

Теперь я сталкиваюсь с проблемой, возвращаясь к step 2 тому месту, где я делал $.ajax вызов. Что продолжает происходить, так это то, что kendo.bind запускается до $.ajax завершения. Я могу поместить это в $.ajax({}).done(); функцию, и для этой конкретной конкретной страницы, которая действительно работает, но будет много других ситуаций, когда это не подходит.

Что я пробовал

Во-первых, я поясню, что jsdeferred документация для меня очень неясна, поскольку дословное выполнение ее примеров на самом деле не работает. Мне постоянно говорят об этом next is not defined и тому подобное. В конце концов я понял, что Deferred. перед первым вызовом у вас должно быть неявное next значение.

Итак, вот что, как я думал, произойдет…

 var viewModel = new kendo.data.ObservableObject({
   // various view model properties defined
});

Deferred.define();

next(function() { // let's call this STEP 1
   $.ajax({
      // data for ajax to controller
   }).done(function(result) {
      // perform operations with result
   });
}).
next(function() { // let's call this STEP 2
   $('#dropdownlist_target').kendoDropDownList({
      // parameters, remote data source for drop down list, etc.
   }).data("kendoDropDownList");
}).
next(function() { // let's call this STEP 3
   $('form').validate({
      // any extra form validation stuff
   });
}). 
next(function(){ // let's call this STEP 4
   kendo.bind('body', viewModel);
});
  

Я полагал, что каждый из них будет выполняться один за другим, когда предыдущий будет завершен. Но это не то, что происходит. STEP 1 все еще находится в процессе выборки во время STEP 2, 3 и 4 выполняется.

Похоже, это ничем не отличается от способа выполнения кода без jsdeferred библиотеки. Так что я очень смущен и был бы очень рад некоторой помощи здесь. В принципе, мне нужно STEP 1 быть полностью законченным до STEP 2 запуска.

Ответ №1:

Проблема в том, что next() ожидается, что вы вернете то, что вы хотите, чтобы оно ожидало. На первом шаге вы ничего не возвращаете. jsdeferred поэтому предполагается, что вы выполняли синхронную операцию (которая уже завершена), и поэтому она продолжается с шага 2.

Вместо этого верните jQuery.Deferred() возвращенное из $.ajax() вызова. затем jsdeferred будет ждать завершения, прежде чем выполнить шаг 2.


Независимо от этого, я бы сбросил jsdeferred . Как вы поняли, jQuery имеет полноценную отложенную реализацию. Я не уверен, что jsdeferred привносит в вечеринку.

Использование $.ajax().done(r) не является небрежным. Асинхронное поведение является ядром языков, управляемых событиями, и JavaScript является одним из них. Примите это, или вы очень рано облысеете, пытаясь избежать этого.

Если вы вернетесь к отложенной реализации jQuery, возможно, вам захочется then() предоставить вам семантику next() ;

 $.ajax({
   // data for ajax to controller
}).done(function(result) {
   // perform operations with result
}).then(function () {
    $('#dropdownlist_target').kendoDropDownList({
       // parameters, remote data source for drop down list, etc.
    }).data("kendoDropDownList");

    $('form').validate({
       // any extra form validation stuff
    });

    kendo.bind('body', viewModel);
}).then(function () {
    // Note you can chain then()'s as well.
});
  

Комментарии:

1. Когда я говорю "sloppy" , я не имею в виду плохой код . Я имею в виду, что лично у меня много проблем с пониманием того, когда что-то запускается — я еще не очень хорошо разбираюсь во всех асинхронных вещах, поэтому для меня это получается неаккуратно, потому что я все еще учусь правильно управлять этим.

2. И да, мне нужно научиться делать это чаще. Я полностью согласен — иногда это просто сложный процесс.

3. Привет, @Matt, это было идеально. Вы точно поняли, чего я пытался достичь, и презентация легко читается. Я думал done , что это эксклюзивно для $.ajax , и именно здесь я заблудился. Это упростит написание большого количества кода!

4. @Ciel: Ах, извините… Я неверно истолковал то, что вы имели в виду :). Объект jqXHR (который возвращает метод ajax() jQuery) предоставляет те же методы, что и jQuery. Обещание Deferred() будет. У jQuery есть документация для всех этих методов и типов, которые могут помочь ему разобраться, хотя я признаю, что документация jQuery для Deferreds в целом — дерьмо. Однако хорошей новостью является то, что концепция promise и deferreds довольно стандартизирована, поэтому любая документация, которую вы можете найти по этому вопросу, может помочь понять идею. Извините, я не могу предоставить никаких ссылок, я нахожусь на своем телефоне, но я надеюсь, что это поможет!

5. Да, я попытался прочитать их документацию deferred , и promise , и я думаю, что кровь начала стрелять из моих ушей.

Ответ №2:

Вы можете просто использовать then метод для своего $.ajax() результата так же, как вы используете next помощник jsDeferred. Вообще говоря, then это более гибкий метод, чем done . И, как отметил Мэтт в своем ответе, в программировании на основе обещаний распространенной ошибкой является забывание return нового обещания в обработчике, что приводит к его преждевременному разрешению undefined вместо ожидания нового обещания.

 $.ajax({  // let's call this STEP 1
   // data for ajax to controller
}).
then(function(result) {
   // perform operations with result
}).
then(function() { // let's call this STEP 2
   $('#dropdownlist_target').kendoDropDownList({
      // parameters, remote data source for drop down list, etc.
   }).data("kendoDropDownList");
}).
then(function() { // let's call this STEP 3
   $('form').validate({
      // any extra form validation stuff
   });
}). 
done(function(){ // let's call this STEP 4
   kendo.bind('body', viewModel);
});
  

Обратите внимание, что в моем рефакторинге все эти then s будут выполняться немедленно подряд, если не будет возвращено новое обещание. Так что вы можете также объединить их.

then принимает функцию, которая либо возвращает значение, либо обещание, и возвращает новое обещание. Если его функция возвращает значение, новое обещание немедленно разрешается с этим значением. Если его функция вернула обещание, то это обещание передается как новое обещание. Обратите внимание, что jQuery then работает только таким образом, начиная с версий jQuery> = 1.8.

Комментарии:

1. Эй, это также было полезно в сочетании с принятым ответом. Большое вам спасибо. Если я могу спросить, возможно ли выполнить такую цепочку с любым другим типом объекта, кроме простого $.ajax вызова?

2. Абсолютно! Объект jqXHR, возвращаемый с помощью ajax , отвечает на те же методы, что и обещание jQuery. Вы можете сделать это самостоятельно, используя $.Deferred .

3. Да, я пытаюсь прочитать $.Deferred , но, черт возьми, у меня кружится голова. Я начинаю понимать, почему jsdeferred было написано. Я думаю, что jsdeferred это было сделано до $.Deferred того, как стало большей частью jQuery (хотя я не могу это доказать).

4. Я настоятельно рекомендую прочитать gist.github.com/domenic/3889970 чтобы по-настоящему усвоить, как работают обещания. Тогда вам действительно просто нужно понять два основных способа создания promise. Существует функциональный способ, с помощью которого вам передаются разрешающие и отклоняющие обратные вызовы, как показано здесь ( github.com/cujojs/when/blob/master/docs/api.md#whenpromise ). Тогда есть способ ООП, типичный $.Deferred для и здесь ( github.com/cujojs/when/blob/master/docs/api.md#whendefer ), в котором вы используете методы отложенного объекта с методами для разрешения и отклонения обещания.

5. @Ciel ^ (чтобы привлечь ваше внимание)