如何让javascript代码按顺序执行*

时间:2010-04-14 13:18:48

标签: javascript ajax order-of-execution

好的,所以我很欣赏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 个答案:

答案 0 :(得分:14)

问题出现是因为AJAX本质上是异步的。这意味着updateStatus()调用确实按顺序执行,但在从AJAX请求中重新获取任何数据之前,立即返回并且JS解释器到达hideLoader()

您应该在AJAX调用完成的事件上执行hideLoader()

答案 1 :(得分:13)

如果您正在进行AJAX编程,则需要将JavaScript视为基于事件而非过程。您必须等到第一个调用完成后再执行第二个调用。这样做的方法是将第二个调用绑定到第一个调用完成后触发的回调。如果不了解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
  });

}

答案 2 :(得分:10)

我认为您需要做的就是在代码中使用它:

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.");
            }  
        });

显然有些需要为XMLJSON等进行更改,但async: false,是告诉JS引擎等待成功调用返回的主要点(或者{{1}}失败取决于)然后继续。 请记住,这有一个缺点,这就是整个页面变得无响应,直到ajax返回!通常在几毫秒内,这不是一个大交易,但可能需要更长的时间。

希望这是正确的答案,它可以帮助你:)

答案 3 :(得分:5)

我们的一个项目中有类似的东西,我们通过使用计数器来解决它。如果为每次调用updateStatus增加计数器并在AJAX请求的响应函数中减少它(取决于您正在使用的AJAX JavaScript库)。

计数器达到零后,所有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
}

答案 4 :(得分:2)

这与代码的执行顺序无关。

加载程序映像永远不会显示的原因是UI在您的函数运行时不会更新。如果您在UI中进行了更改,则在退出该函数并将控件返回到浏览器之前,它们不会显示。

您可以在设置图像后使用超时,使浏览器有机会在开始其余代码之前更新UI:

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请求是异步的,则该函数不会等待响应。当浏览器收到响应时,将运行负责响应的函数。如果要在收到响应后隐藏加载程序映像,则必须在最后一个响应处理程序函数运行时执行此操作。由于回复不必按照您发送请求的顺序到达,因此您需要计算最后一个回复时您需要知道多少回复。

答案 5 :(得分:1)

安装Firebug,然后在showLoader,updateStatus和hideLoader中添加这样的一行:

Console.log("event logged");

您将在控制台窗口中看到对您的功能的调用,它们将按顺序排列。问题是,你的“updateStatus”方法是做什么的?

据推测,它会启动后台任务,然后返回,这样您就可以在任何后台任务完成之前调用hideLoader。您的Ajax库可能有一个“OnComplete”或“OnFinished”回调 - 从那里调用以下updateStatus。

答案 6 :(得分:1)

正如其他人所指出的,你不想做同步操作。拥抱Async,这就是AJAX中的A所代表的。

我想提一下同步v / s异步的一个很好的类比。你可以阅读entire post on the GWT forum,我只是包括相关的类比。

  

想象一下,如果你......

     

你正坐在沙发上看着   电视,知道你不在   啤酒,你问你的配偶取悦   跑到酒店和   拿你一些。一见到你   你的配偶走出前门,   你从沙发上爬起来并且滚动   进厨房打开   冰箱。令你惊讶的是,没有   啤酒!

     

当然,没有啤酒,你的   配偶还在旅途中   酒品店。你必须等到   他会在你预期之前回来   喝啤酒。

但是,你说你想让它同步吗?想象一下......

  

......配偶走出门......现在,   你身边的整个世界都停止了   不要呼吸,回答   门,或看完你的节目   虽然他跑过城镇   拿你的啤酒。你只能坐下来   没有移动肌肉,而且   变蓝,直到你输了   意识......唤醒一些   无限时间后被包围   EMT和配偶说哦,嘿,我   拿了你的啤酒。

当你坚持进行同步服务器调用时,就会发生这种情况。

答案 7 :(得分:0)

将updateStatus调用移动到另一个函数。使用新函数作为目标来调用setTimeout。

如果您的ajax请求是异步的,您应该有一些东西可以跟踪哪些已完成。每个回调方法都可以为自己设置一个“已完成”的标志,并检查它是否是最后一个。如果是,那就让它调用hideLoader。

答案 8 :(得分:0)

处理所有异步请求的最佳解决方案之一是'承诺' 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
    }  
  });  
});

使用此方法,您可以根据需要处理所有异步操作。