Подтвердить что ты не робот

Как связать запросы ajax?

Мне нужно взаимодействовать с удаленным api, что заставляет меня запросить цепочку. Thats callback-hell в асинхронном режиме:

// pseudocode: ajax(request_object, callback)
ajax(a, function() {
  ajax(b(a.somedata), function() {
    ajax(c(b.somedata), function() {
      c.finish()
    }
  }) 
})

Это было бы гораздо более читаемым в режиме синхронизации:

sjax(a)
sjax(b(a.somedata))
sjax(c(b.somedata))
c.finish()

Но Sjax злой:) Как это сделать в nice не-злом и читаемом?

4b9b3361

Ответ 1

Не используйте анонимные функции. Дайте им имена. Я не знаю, можете ли вы сделать то, что я написал ниже, хотя:

var step_3 = function() {
    c.finish();
};

var step_2 = function(c, b) {
    ajax(c(b.somedata), step_3);
};

var step_1 = function(b, a) {
  ajax(b(a.somedata), step_2);
};

ajax(a, step_1);

Ответ 2

У вас может быть одна функция, которой передается целое число, чтобы указать, на каком этапе находится запрос, а затем используйте оператор switch, чтобы выяснить, какой запрос необходимо выполнить следующим образом:

function ajaxQueue(step) {
  switch(step) {
    case 0: $.ajax({
              type: "GET",
              url: "/some/service",
              complete: function() { ajaxQueue(1); } 
    }); break;
    case 1: $.ajax({
              type: "GET",
              url: "/some/service",
              complete: function() { ajaxQueue(2); }
            }); break;
    case 2: $.ajax({
              type: "GET",
              url: "/some/service",
              complete: function() { alert('Done!'); }
            }); break;
  }
}

ajaxQueue(0);

Надеюсь, что это поможет!

Ответ 3

Эта функция должна объединить список запросов ajax, если обратные вызовы всегда возвращают параметры, необходимые для следующего запроса:

function chainajax(params, callbacks) {
  var cb = shift(callbacks);
  params.complete = function() {
    var newparams = cb(arguments);
    if (callbacks)
      chainajax(newparams, callbacks);
  };
  $.ajax(params);
};

Вы можете определить эти функции обратного вызова отдельно, а затем соединить их вместе:

function a(data) {
  ...
  return {type: "GET", url: "/step2.php?foo"}
};
// ...
function d(data) { alert("done!"); };

chainajax({type: "GET", url: "/step1.php"},
  [a, b, c, d]);

Вы также можете объявить функции "inline" в вызове chainajax, но это может немного запутать.

Ответ 4

Возможно, что вы можете сделать, это написать серверную функцию обертки. Таким образом, ваш javascript выполняет только один асинхронный вызов на ваш собственный веб-сервер. Затем ваш веб-сервер использует завиток (или urllib и т.д.) Для взаимодействия с удаленным API.

Ответ 5

Обновление: я узнал для этого лучший ответ, если вы используете jQuery, см. мое обновление под заголовком: Использование jQuery Deffered

Старый ответ:

Вы также можете использовать Array.reduceRight (когда он доступен), чтобы обернуть вызовы $.ajax и преобразовать список, например: [resource1, resource2] в $.ajax({url:resource1,success: function(...) { $ajax({url: resource2... (трюк, который я изучил из Haskell, и его fold/foldRight функция).

Вот пример:

var withResources = function(resources, callback) {
    var responses = [];
    var chainedAjaxCalls = resources.reduceRight(function(previousValue, currentValue, index, array) {
        return function() {
            $.ajax({url: currentValue, success: function(data) {
                responses.push(data);
                previousValue();
            }})
        }
    }, function() { callback.apply(null, responses); });
    chainedAjaxCalls();
};

Затем вы можете использовать:

withResources(['/api/resource1', '/api/resource2'], function(response1, response2) {
    // called only if the ajax call is successful with resource1 and resource2
});

Использование jQuery Deffered

Если вы используете jQuery, вы можете использовать jQuery Deffered, используя функцию jQuery.when():

 jQuery.when($.get('/api/one'), $.get('/api/two'))
       .done(function(result1, result2) { 
              /* one and two is done */
        });

Ответ 6

Посмотрите этот пункт часто задаваемых вопросов на сайте jQuery. Специально ссылка обратного вызова и полный метод.

То, что вы хотите, это данные из A, которые должны быть переданы B и B, переданным C. Таким образом, вы выполнили бы обратный вызов при завершении.

Я не пробовал это, хотя.

Ответ 7

Я считаю, что реализация конечного автомата сделает код более читаемым:

var state = -1;
var error = false;

$.ajax({success: function() { 
                  state = 0;
                  stateMachine(); },
        error: function() {
                  error = true;
                  stateMachine();
        }});

function stateMachine() {
  if (error) {
     // Error handling
     return;
  }

  if (state == 0) {
    state = 1;
    // Call stateMachine again in an ajax callback
  }
  else if (state == 1) {

  }
}

Ответ 8

Я сделал метод, используя Promises

// How to setup a chainable queue method
var sequence = Promise.resolve();

function chain(next){
    var promise = new Promise(function(resolve){
        sequence.then(function(){
            next(resolve);
        });	
    });

    sequence = promise;
}

// How to use it
chain(function(next){
    document.write("<p>start getting config.json</p>");
    setTimeout(function(){
    	document.write("<p>Done fetching config.json</p>");
        next();
    }, 3000);
});

chain(function(next){
    document.write("<p>start getting init.js</p>")
    setTimeout(function(){
        document.write("<p>starting eval scripting</p>");
        next();
    }, 3000);
});

chain(function(next){
    document.write("<p>Everything is done</p>");
});

Ответ 9

Полный обратный вызов - это то, что вы ищете:

$.ajax({
     type: 'post',
     url: "www.example.com",
     data: {/* Data to be sent to the server. It is converted to a query string, if not already a string. It appended to the url for GET-requests. */},
     success:
          function(data) {
              /* you can also chain requests here. will be fired if initial request is successful but will be fired before completion. */
          },
    complete: 
         function() {
             /* For more a more synchronous approach use this callback. Will be fired when first function is completed. */
         }
});