23

好的,所以我很欣赏 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函数按照我在页面上写的顺序执行......

4

9 回答 9

16

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

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

于 2010-04-14T13:22:27.307 回答
14

如果您在进行 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
  });

}

于 2010-04-14T13:33:54.427 回答
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 引擎等到成功调用返回(或失败取决于)然后继续。请记住,这样做有一个缺点,那就是整个页面变得无响应,直到 ajax 返回!!!通常在几毫秒内,这不是什么大问题,但可能需要更长的时间。

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

于 2011-07-14T12:35:42.193 回答
5

我们的一个项目中有类似的东西,我们通过使用计数器解决了它。如果您在 AJAX 请求的响应函数中增加每次调用的计数器updateStatus并减少它(取决于您使用的 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
}
于 2010-04-14T13:34:59.173 回答
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 请求是异步的,则该函数不会等待响应。处理响应的函数将在浏览器收到响应时运行。如果要在收到响应后隐藏加载程序图像,则必须在最后一个响应处理程序函数运行时执行此操作。由于响应不必按照您发送请求的顺序到达,因此您需要计算当最后一个响应到来时您知道多少响应。

于 2010-04-14T13:27:48.660 回答
1

正如其他人指出的那样,您不想进行同步操作。拥抱异步,这就是 AJAX 中的 A 所代表的意思。

我只想提一个关于同步与异步的很好的类比。你可以在 GWT 论坛上阅读整篇文章,我只是包括相关的类比。

想象一下,如果你...

你坐在沙发上看电视,知道你没有啤酒了,你让你的配偶跑到酒铺给你拿一些。一旦你看到你的配偶走出前门,你就会从沙发上站起来,蹒跚地走进厨房,打开冰箱。令您惊讶的是,没有啤酒!

好吧,当然没有啤酒,你的配偶还在去酒铺的路上。你必须等到[s]他回来之后才能期待喝啤酒。

但是,你说你想要它同步?再想象...

......配偶走出门......现在,你周围的整个世界都停止了,你没有呼吸,没有开门,也没有看完你的节目,而[s]他跑过城镇去拿啤酒。你只是坐在那里一动不动,脸色发青,直到你失去知觉……过了一段时间后,被 EMT 包围,一个配偶说哦,嘿,我拿了你的啤酒。

这正是您坚持进行同步服务器调用时发生的情况。

于 2010-04-14T14:17:47.100 回答
1

安装 Firebug,然后在 showLoader、updateStatus 和 hideLoader 中添加如下一行:

Console.log("event logged");

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

大概它会启动一个后台任务,然后返回,因此您将在任何后台任务完成之前调用 hideLoader。您的 Ajax 库可能有一个“OnComplete”或“OnFinished”回调 - 从那里调用以下 updateStatus。

于 2010-04-14T13:33:13.907 回答
0

将 updateStatus 调用移至另一个函数。以新函数为目标调用 setTimeout。

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

于 2010-04-14T13:36:18.317 回答
0

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

承诺

如果您有 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
    }  
  });  
});

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

于 2017-12-08T18:00:09.480 回答