Как сделать так, чтобы код JavaScript выполнялся * по порядку * - PullRequest
19 голосов
/ 14 апреля 2010

Хорошо, поэтому я ценю, что Javascript не является C # или PHP, но я продолжаю возвращаться к проблеме в Javascript - не с самим JS, а с моим использованием.

У меня есть функция:

function updateStatuses(){

showLoader() //show the 'loader.gif' in the UI

updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');

hideLoader(); //hide the 'loader.gif' in the UI

}

Дело в том, что из-за сильного желания Javascript прыгнуть вперед в коде загрузчик никогда не появляется, потому что сразу после этого запускается функция hideLoader.

Как я могу это исправить? Или, другими словами, как я могу заставить функцию javascript выполняться в том порядке, в котором я написал ее на странице ...

Ответы [ 9 ]

14 голосов
/ 14 апреля 2010

Проблема возникает из-за того, что AJAX по своей природе асинхронен. Это означает, что updateStatus() вызовы действительно выполняются по порядку, но немедленно возвращаются, и интерпретатор JS достигает hideLoader(), прежде чем какие-либо данные будут получены из запросов AJAX.

Вы должны выполнить hideLoader() для события, когда вызовы AJAX завершены.

13 голосов
/ 14 апреля 2010

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

showLoader();

  updateStatus('cron1', function() {
    updateStatus('cron2', function() {
      updateStatus('cron3', function() {
        updateStatus('cronEmail', function() {
          updateStatus('cronHourly', function() {
            updateStatus('cronDaily', funciton() { hideLoader(); })
          })
        })
      })
    })
  })
});

Идея состоит в том, что updateStatus принимает свой обычный аргумент плюс функцию обратного вызова, которую нужно выполнить после завершения. Это довольно распространенный способ передачи функции для запуска onComplete в функцию, которая обеспечивает такой хук.

Обновление

Если вы используете jQuery, вы можете прочитать о $.ajax() здесь: http://api.jquery.com/jQuery.ajax/

Ваш код, вероятно, выглядит примерно так:

function updateStatus(arg) {
  // processing

  $.ajax({
     data : /* something */,
     url  : /* something */
  });

  // processing
}

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

function updateStatus(arg, onComplete) {
  $.ajax({
    data : /* something */,
    url  : /* something */,
    complete : onComplete // called when AJAX transaction finishes
  });
* *} Тысяча двадцать-один
10 голосов
/ 14 июля 2011

Я думаю, все, что вам нужно сделать, это включить в свой код:

async: false,

Итак, ваш Ajax-вызов будет выглядеть так:

jQuery.ajax({
            type: "GET",
            url: "something.html for example",
            dataType: "html",
            async: false,
            context: document.body,
            success: function(response){

                //do stuff here

            },
            error: function() {
                alert("Sorry, The requested property could not be found.");
            }  
        });

Очевидно, что некоторые из этого должны быть изменены на XML, JSON и т. Д., Но здесь async: false, является основным пунктом, который говорит движку JS ждать, пока не будет возвращен (или не выполнен) вызов успешного вызова, а затем перенести на. Помните, что в этом есть и обратная сторона: вся страница перестает отвечать, пока не вернется Ajax !!! обычно в течение миллисекунд, что не имеет большого значения, но МОЖЕТ занять больше времени.

Надеюсь, что это правильный ответ, и он поможет вам:)

5 голосов
/ 14 апреля 2010

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

Как только счетчик достигнет нуля, все AJAX-запросы будут выполнены, и вы сможете позвонить hideLoader().

Вот пример:

var loadCounter = 0;

function updateStatuses(){
    updateStatus('cron1'); //performs an ajax request to get the status of something
    updateStatus('cron2');
    updateStatus('cron3');    
    updateStatus('cronEmail');
    updateStatus('cronHourly');
    updateStatus('cronDaily');
}

function updateStatus(what) {
    loadCounter++;

    //perform your AJAX call and set the response method to updateStatusCompleted()
}

function updateStatusCompleted() {
    loadCounter--;
    if (loadCounter <= 0)
        hideLoader(); //hide the 'loader.gif' in the UI
}
2 голосов
/ 14 апреля 2010

Это не имеет ничего общего с порядком выполнения кода.

Причина, по которой изображение загрузчика никогда не отображается, заключается в том, что пользовательский интерфейс не обновляется во время работы вашей функции. Если вы сделаете изменения в пользовательском интерфейсе, они не появятся, пока вы не выйдете из функции и не вернете управление браузеру.

Вы можете использовать тайм-аут после установки изображения, давая браузеру возможность обновить пользовательский интерфейс перед запуском остальной части кода:

function updateStatuses(){

  showLoader() //show the 'loader.gif' in the UI

  // start a timeout that will start the rest of the code after the UI updates
  window.setTimeout(function(){
    updateStatus('cron1'); //performs an ajax request to get the status of something
    updateStatus('cron2');
    updateStatus('cron3');
    updateStatus('cronEmail');
    updateStatus('cronHourly');
    updateStatus('cronDaily');

    hideLoader(); //hide the 'loader.gif' in the UI
  },0);
}

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

1 голос
/ 14 апреля 2010

Как уже отмечали другие, вы не хотите выполнять синхронную операцию. Охватите Async, вот что означает A в AJAX.

Я просто хотел бы привести отличную аналогию с sync v / s async. Вы можете прочитать весь пост на форуме GWT , я просто включаю соответствующие аналогии.

Представь, если ты будешь ...

Вы сидите на диване и смотрите Телевизор, и зная, что ты вне пиво, попросите своего супруга, пожалуйста бежать в винный магазин и принести тебе немного. Как только вы увидите ваш супруг выходит из парадной двери, ты встаешь с дивана и катишься на кухню и откройте холодильник. К вашему удивлению, нет пиво!

Ну, конечно, нет пива, ваш супруга все еще находится в поездке в винный магазин. Ты должен подождать, пока он возвращается раньше, чем вы можете ожидать пить пиво.

Но вы говорите, что хотите синхронно? Представь себе снова ...

... супруга выходит за дверь ... сейчас, весь мир вокруг тебя останавливается, ты не дышать, ответить на дверь, или закончите смотреть свое шоу в то время как он бежит через город к принеси свое пиво Вы просто можете сидеть там не двигаются мышцы, а синий, пока не проиграешь сознание ... просыпаться неопределенное время спустя в окружении ЕМЦ и супруга говорят о, эй, я получил ваше пиво.

Именно так и происходит, когда вы настаиваете на синхронном вызове сервера.

1 голос
/ 14 апреля 2010

Установите Firebug, затем добавьте такую ​​строку в каждый из showLoader, updateStatus и hideLoader:

Console.log("event logged");

Вы увидите перечисленные в окне консоли вызовы вашей функции, и они будут в порядке. Вопрос в том, что делает ваш метод updateStatus?

Предположительно, он запускает фоновую задачу, а затем возвращается, так что вы достигнете вызова hideLoader до того, как завершится любая из фоновых задач. Ваша Ajax-библиотека, вероятно, имеет обратный вызов OnComplete или OnFinished - оттуда вызовите следующий updateStatus.

0 голосов
/ 08 декабря 2017

Одним из лучших решений для обработки всех асинхронных запросов является 'Promise' .
Объект Promise представляет возможное завершение (или сбой) асинхронной операции.

Пример:

let myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR or an HTML5 API.
  setTimeout(function(){
    resolve("Success!"); // Yay! Everything went well!
  }, 250);
});  

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log("Yay! " + successMessage);
});

Promise

Если у вас есть 3 асинхронные функции и вы планируете работать по порядку, выполните следующие действия:

let FirstPromise = new Promise((resolve, reject) => {
    FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {

});
let ThirdPromise = new Promise((resolve, reject) => {

});
FirstPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("First! ");
        SecondPromise.resolve("Second!");
    },
    error: function() {
        //handle your error
    }  
  });           
});
SecondPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("Second! ");
        ThirdPromise.resolve("Third!");
    },
    error: function() {
       //handle your error
    }  
  });    
});
ThirdPromise.then((successMessage) => {
  jQuery.ajax({
    type: "type",
    url: "url",
    success: function(response){
        console.log("Third! ");
    },
    error: function() {
        //handle your error
    }  
  });  
});

При таком подходе вы можете обрабатывать все асинхронные операции по своему усмотрению.

0 голосов
/ 14 апреля 2010

переместить вызовы updateStatus в другую функцию. сделать вызов setTimeout с новой функцией в качестве цели.

если ваши ajax-запросы асинхронные, вам нужно что-то отследить, какие из них были выполнены. каждый метод обратного вызова может установить где-нибудь флаг «завершено» и проверить, является ли он последним. если это так, то вызовите hideLoader.

...