简体   繁体   English

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

[英]How do you make javascript code execute *in order*

Okay, so I appreciate that Javascript is not C# or PHP, but I keep coming back to an issue in Javascript - not with JS itself but my use of it. 好的,所以我很欣赏Javascript不是C#或PHP,但我一直回到Javascript中的一个问题 - 不是JS本身,而是我使用它。

I have a function: 我有一个功能:

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

}

Thing is, owing to Javascript's burning desire to jump ahead in the code, the loader never appears because the 'hideLoader' function runs straight after. 事实上,由于Javascript强烈希望在代码中跳跃,加载器永远不会出现,因为'hideLoader'函数直接运行。

How can I fix this? 我怎样才能解决这个问题? Or in other words, how can I make a javascript function execute in the order I write it on the page... 或者换句话说,我怎么能按照我在页面上写的顺序执行javascript函数...

The problem occurs because AJAX is in its nature asynchronus. 出现此问题是因为AJAX本质上是异步的。 This means that the updateStatus() calls are indeed executed in order but returns immediatly and the JS interpreter reaches hideLoader() before any data is retreived from the AJAX requests. 这意味着updateStatus()调用确实按顺序执行但是立即返回,并且JS解释器在从AJAX请求中检索任何数据之前到达hideLoader()

You should perform the hideLoader() on an event where the AJAX calls are finished. 您应该在AJAX调用完成的事件上执行hideLoader()

You need to think of JavaScript as event based rather than procedural if you're doing AJAX programming. 如果你正在进行AJAX编程,你需要将JavaScript视为基于事件而不是程序。 You have to wait until the first call completes before executing the second. 您必须等到第一个调用完成后再执行第二个调用。 The way to do that is to bind the second call to a callback that fires when the first is finished. 这样做的方法是将第二个调用绑定到第一个调用完成后触发的回调。 Without knowing more about the inner workings of your AJAX library (hopefully you're using a library) I can't tell you how to do this, but it will probably look something like this: 如果不了解AJAX库的内部工作原理(希望你使用的是库),我不能告诉你如何做到这一点,但它可能看起来像这样:

showLoader();

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

The idea is, updateStatus takes its normal argument, plus a callback function to execute when it's finished. 这个想法是, updateStatus接受它的正常参数,加上一个回调函数在它完成时执行。 It's a reasonably common pattern to pass a function to run onComplete into a function which provides such a hook. 将函数运行onComplete传递给提供这种钩子的函数是一种相当常见的模式。

Update 更新

If you're using jQuery, you can read up on $.ajax() here: http://api.jquery.com/jQuery.ajax/ 如果你正在使用jQuery,你可以在这里阅读$.ajax()http//api.jquery.com/jQuery.ajax/

Your code probably looks something like this: 您的代码可能看起来像这样:

function updateStatus(arg) {
  // processing

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

  // processing
}

You can modify your functions to take a callback as their second parameter with something like this: 您可以修改函数以将回调作为第二个参数,如下所示:

function updateStatus(arg, onComplete) {
  $.ajax({
    data : /* something */,
    url  : /* something */,
    complete : onComplete // called when AJAX transaction finishes
  });

} }

I thinks all you need to do is have this in your code: 我认为您需要做的就是在代码中使用它:

async: false,

So your Ajax call would look like this: 所以你的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.");
            }  
        });

Obviously some of this need to change for XML , JSON etc but the async: false, is the main point here which tell the JS engine to wait until the success call have returned (or failed depending) and then carry on. 显然,其中一些需要更改XMLJSON等,但async: false,这是告诉JS引擎等待成功调用返回(或失败依赖)然后继续的主要观点。 Remember there is a downside to this, and thats that the entire page becomes unresponsive until the ajax returns!!! 请记住,这有一个缺点,这就是整个页面变得反应迟钝,直到ajax返回! usually within milliseconds which is not a big deals but COULD take longer. 通常在几毫秒内这不是一个大交易,但可能需要更长的时间。

Hope this is the right answer and it helps you :) 希望这是正确的答案,它可以帮助你:)

We have something similar in one of our projects, and we solved it by using a counter. 我们的一个项目中有类似的东西,我们通过使用计数器来解决它。 If you increase the counter for each call to updateStatus and decrease it in the AJAX request's response function (depends on the AJAX JavaScript library you're using.) 如果为每次调用updateStatus增加计数器并在AJAX请求的响应函数中减少它(取决于你正在使用的AJAX JavaScript库。)

Once the counter reaches zero, all AJAX requests are completed and you can call hideLoader() . 一旦计数器达到零,所有AJAX请求都完成,你可以调用hideLoader()

Here's a sample: 这是一个示例:

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
}

This has nothing to do with the execution order of the code. 这与代码的执行顺序无关。

The reason that the loader image never shows, is that the UI doesn't update while your function is running. 加载程序映像永远不会显示的原因是UI在您的函数运行时不会更新。 If you do changes in the UI, they don't appear until you exit the function and return control to the browser. 如果在UI中进行了更改,则在退出函数并将控件返回到浏览器之前,它们不会显示。

You can use a timeout after setting the image, giving the browser a chance to update the UI before starting rest of the code: 您可以在设置图像后使用超时,使浏览器有机会在开始其余代码之前更新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);
}

There is another factor that also can make your code appear to execute out of order. 还有另一个因素也可能使您的代码看起来无序执行。 If your AJAX requests are asynchronous, the function won't wait for the responses. 如果您的AJAX请求是异步的,则该函数不会等待响应。 The function that takes care of the response will run when the browser receives the response. 当浏览器收到响应时,将运行负责响应的函数。 If you want to hide the loader image after the response has been received, you would have to do that when the last response handler function runs. 如果要在收到响应后隐藏加载程序映像,则必须在最后一个响应处理程序函数运行时执行此操作。 As the responses doesn't have to arrive in the order that you sent the requests, you would need to count how many responses you got to know when the last one comes. 由于响应不必按照您发送请求的顺序到达,因此您需要计算在最后一个响应时您需要知道多少响应。

Install Firebug, then add a line like this to each of showLoader, updateStatus and hideLoader: 安装Firebug,然后将这样的行添加到showLoader,updateStatus和hideLoader中:

Console.log("event logged");

You'll see listed in the console window the calls to your function, and they will be in order. 您将看到在控制台窗口中列出了对您的函数的调用,它们将按顺序排列。 The question, is what does your "updateStatus" method do? 问题是,你的“updateStatus”方法是做什么的?

Presumably it starts a background task, then returns, so you will reach the call to hideLoader before any of the background tasks finish. 大概它会启动一个后台任务,然后返回,这样你就可以在任何后台任务完成之前调用hideLoader。 Your Ajax library probably has an "OnComplete" or "OnFinished" callback - call the following updateStatus from there. 您的Ajax库可能有一个“OnComplete”或“OnFinished”回调 - 从那里调用以下updateStatus。

As others have pointed out, you don't want to do a synchronous operation. 正如其他人所指出的那样,您不希望进行同步操作。 Embrace Async, that's what the A in AJAX stands for. 拥抱Async,这就是AJAX中的A所代表的。

I would just like to mention an excellent analogy on sync v/s async. 我想提一下同步v / s异步的一个很好的类比。 You can read the entire post on the GWT forum , I am just including the relevant analogies. 你可以在GWT论坛上阅读整篇文章 ,我只是包括相关的类比。

Imagine if you will ... 想象一下,如果你...

You are sitting on the couch watching TV, and knowing that you are out of beer, you ask your spouse to please run down to the liquor store and fetch you some. 你坐在沙发上看电视,知道你没有啤酒,你问你的配偶请跑到酒店去取一些。 As soon as you see your spouse walk out the front door, you get up off the couch and trundle into the kitchen and open the fridge. 一旦你看到你的配偶走出前门,你就从沙发上爬起来,然后滚进厨房,打开冰箱。 To your surprise, there is no beer! 令你惊讶的是,没有啤酒!

Well of course there is no beer, your spouse is still on the trip to the liquor store. 当然,没有啤酒,你的配偶仍然在酒店的旅行。 You've gotta wait until [s]he returns before you can expect to have a beer. 你必须等到他回来之前你才可以喝啤酒。

But, you say you want it synchronous? 但是,你说你希望它同步吗? Imagine again ... 想象一下......

... spouse walks out the door ... now, the entire world around you stops, you don't get to breath, answer the door, or finish watching your show while [s]he runs across town to fetch your beer. ......配偶走出门......现在,你周围的整个世界都停了下来,你没有得到呼吸,回答门,或者在他穿过城镇去取啤酒的时候看完你的节目。 You just get to sit there not moving a muscle, and turning blue until you lose consciousness ... waking up some indefinite time later surrounded by EMTs and a spouse saying oh, hey, I got your beer. 你只是坐在那里没有移动肌肉,变成蓝色直到你失去知觉......后来被EMT和一个配偶包围的无限时间醒来哦,嘿,我喝了你的啤酒。

That's exactly what happens when you insist on doing a synchronous server call. 这正是您坚持进行同步服务器调用时会发生的情况。

move the updateStatus calls to another function. 将updateStatus调用移动到另一个函数。 make a call setTimeout with the new function as a target. 使用新函数作为目标进行调用setTimeout。

if your ajax requests are asynchronous, you should have something to track which ones have completed. 如果您的ajax请求是异步的,您应该有一些东西来跟踪哪些已完成。 each callback method can set a "completed" flag somewhere for itself, and check to see if it's the last one to do so. 每个回调方法都可以为自己设置一个“已完成”的标志,并检查它是否是最后一个。 if it is, then have it call hideLoader. 如果是,那就让它调用hideLoader。

One of the best solutions for handling all async requests is the 'Promise' . 处理所有异步请求的最佳解决方案之一是“Promise”
The Promise object represents the eventual completion (or failure) of an asynchronous operation. Promise对象表示异步操作的最终完成(或失败)。

Example: 例:

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 诺言

If you have 3 async functions and expect to run in order, do as follows: 如果您有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
    }  
  });  
});

With this approach, you can handle all async operation as you wish. 使用此方法,您可以根据需要处理所有异步操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM