упаковка jquery ajax в функцию с возвратом - PullRequest
4 голосов
/ 02 июля 2011

Я пытаюсь сделать некоторые из моих javascript более тестируемыми, в связи с этим я оборачиваю некоторые функции в классах, чтобы смоделировать их в своих тестах.

В любом случае, я пытаюсь завершить свой AJAX, поэтому, насколько это касается моего приложения, оно запрашивает сервис для объекта. Внутренне он будет делать запрос ajax, а затем что-то делать с данными и затем возвращать их.

Так будет ли возможно что-то вроде приведенного ниже примера? (Я сейчас в движении, поэтому не могу попробовать это для себя)

function SomeAjaxService(webServiceUrl)
{
    this.getSomeModel = function(someUniqueId){
        var ajaxOptions = {
            url: webServiceUrl,
            data: {id : someUniqueId},
            Success: function(data) { return new SomeModel(data); }
        };
        $.ajax(ajaxOptions);
    };
}

var ajaxService = new SomeAjaxService("http://someurl");
var myModel = ajaxService.getSomeModel(1);

Поскольку ajax по своей природе асинхронный, я думаю, вы можете установить его как синхронный, но я просто хотел проверить, какие были варианты ...

Ответы [ 4 ]

3 голосов
/ 02 июля 2011

Установка его в синхронный режим - плохая идея, так как вы будете блокировать JavaScript (даже браузер) до тех пор, пока не вернется запрос.Ваш код не будет работать, потому что функция успеха вызывается из контекста, отличного от прямого вызова метода getSomeModel.Наилучшим подходом для вас будет вызвать getSomeModel и передать ему функцию обратного вызова, которая будет выполнена при получении модели.

function SomeAjaxService(webServiceUrl)
{
    this.getSomeModel = function(someUniqueId, callback){
        var ajaxOptions = {
            url: webServiceUrl,
            data: {id : someUniqueId},
            // calls the callback you passed
            Success: function(data) { callback(new SomeModel(data)); }
        };
        // this is asynchronous and doesn't return anything
        $.ajax(ajaxOptions);
    };
}

var ajaxService = new SomeAjaxService("http://someurl");
// your callback down here
var myModel;
ajaxService.getSomeModel(1, function(model) {
    myModel = model;
    // do something with model
});
2 голосов
/ 10 июня 2013

Я тоже сталкивался с этой проблемой, и очевидно, что подход с обратным вызовом работает с объектом jqXHR, который возвращается из вызова $ .ajax (), реализующим интерфейс Promise, и, таким образом, я могу делать отложенные вызовы метода done (или дажеизменить функциональность метода done на более поздний момент времени. Вот один из рабочих примеров:

//some separate js file to be included via a <script src=...></script>
//suppose it's named simpleWrappers.js
function wrapAjax(dataURL,postData) {
  postData = (typeof postData === 'string') ? postData : "";
  respObj = $.ajax({
      url: dataURL
    , data: postData
    , dataType: 'json'
    , type: 'POST'
  });
  return respObj
}

Теперь давайте добавим файл simpleWrappers.js на какую-то страницу со своей собственной функцией готовности документа

//...
<script src="js/simpleWrappers.js"></script>
//...
<script>
  $(document).ready(function() {
    //...
    myData = wrapAjax("scripts/returnCoolData.php?fname=fetchSuperCoolData");
    //now attach a done handler!
    myData.done(function(data,statusText,jqXHR) {
      //we'll do some trivial logging
      console.log("the status of the request was: " + statusText);
      //note, my data comes back in JSON, so I JSON.stringify() the data
      console.log("data returned is: " + JSON.stringify(data));
      //Note: don't expect responseText to be available, as in
      //console.log("responseText was: " + myData.responseText)
      //may well return "responseText was: undefined, see above answers
      //to grasp the calling context issue
    }); 
  }
</script>
1 голос
/ 02 июля 2011

Вы можете установить его синхронность, добавив опцию async: false к вашему вызову, но ваша функция все равно ничего не вернет, потому что оператор return в обратном вызове успеха возвращается из обратный вызов, а не основная функция.

Если я вас правильно понял, то (изменения помечены *):

function SomeAjaxService(webServiceUrl)
{
    this.getSomeModel = function(someUniqueId){
        var rv;                                                   // *
        var ajaxOptions = {
            url: webServiceUrl,
            async: false,                                         // *
            data: {id : someUniqueId},
            Success: function(data) { rv = new SomeModel(data); } // *
        };
        $.ajax(ajaxOptions);
        return rv;                                                // *
    };
}

Но вместо того, чтобы синхронизировать ваши вызовы ajax для тестирования, что может иметь побочные эффекты и сделать тесты недействительными, я настоятельно рекомендую вместо этого сделать вашу среду тестирования асинхронной. Платформа асинхронного тестирования может выполнять синхронные тесты; синхронный тестовый фреймворк не может выполнять асинхронные тесты. Таким образом, структура должна быть асинхронной ...

Я бы также настоятельно посоветовал не использовать собственную тестовую среду. Есть куча из них, которые вы можете выбрать из , не создавая свои собственные.


Обновление : ... но если вы действительно хотите создать свой собственный, вот очень простой пример того, что я имею в виду, что нетрудно сделать фреймворк асинхронным ( live copy ):

jQuery(function($) {

  $("#theButton").click(function() {
    var tests, testIndex, nesting;

    tests = [
      function(cb) { display("I'm test1: sync");  cb(1); },
      function(cb) { display("I'm test2: async"); setTimeout(function() {
        cb(2);
      }, 0); },
      function(cb) { display("I'm test3: sync");  cb(3); },
      function(cb) { display("I'm test4: async"); setTimeout(function() {
        cb(4);
      }, 0); }
    ];

    nesting = 0;
    testIndex = 0;
    doTest();

    function doTest() {
      if (testIndex >= tests.length) {
        return true; // Done
      }
      ++nesting;
      oneTest(tests[testIndex++]);
      --nesting;
      return false;
    }

    function testDone(result) {
      display("Result: " + result);
      if (nesting > 0) {
        // Completion was called synchronously; schedule next
        // test asynchronously to avoid recursing too deeply.
        // You could use a higher number than 0 to allow *some*
        // recursion for efficiency but avoid letting too much
        // occur.
        display("r"); // Just for demonstrating that it happens
        setTimeout(doTest, 0);
      }
      else {
        // We were already called asynchronously, start next
        doTest();
      }
    }

    function oneTest(test) {
      test(testDone);
    }

  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

});
0 голосов
/ 02 июля 2011

ajax-функции будут вызываться, но вы не получите экземпляр someModel в случае успеха, поскольку getSomeModel ничего не возвращает. Решением является использование триггерной функции.

function SomeAjaxService(webServiceUrl)
{
    this.getSomeModel = function(someUniqueId){
        var ajaxOptions = {
            url: webServiceUrl,
            data: {id : someUniqueId},
            Success: function(data) { model = new SomeModel(data); $.trigger("SomeModelInit", model) }
        };
        $.ajax(ajaxOptions);
    };
}

var ajaxService = new SomeAjaxService("http://someurl");
$.bind("SomeModelInit", function (model) {
//process
})

еще один способ проверить html / javascript / css - это селен

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...