【问题标题】:Variables set during $.getJSON function only accessible within function$.getJSON 函数期间设置的变量只能在函数内访问
【发布时间】:2010-12-16 22:09:48
【问题描述】:

这可能更像是一个范围界定问题。我正在尝试在 $.getJSON 函数中设置 JSON 对象,但我需要能够在回调之外使用该对象。

var jsonIssues = {};  // declare json variable

$.getJSON("url", function(data) {
    jsonIssues = data.Issues;
});

// jsonIssues not accessible here

在另一篇文章中提出了类似的问题,并且一致认为我需要对 JSON 对象执行的任何操作都需要在回调函数中完成,并且无法在其他任何地方访问。我真的没有办法在 $.getJSON 回调之外继续访问/操作该 JSON 对象吗?返回变量或设置全局变量怎么样?

如果有任何帮助,我将不胜感激。这似乎不太对...

更新:

尝试将 $.ajax() 异步设置设置为 false,并运行相同的代码,但没有成功。我试过的代码如下:

var jsonIssues = {};  // declare json variable

$.ajax({ async: false });

$.getJSON("url", function(data) {
    jsonIssues = data.Issues;
});

// jsonIssues still not accessible here

另外,我有几个回应说全局变量应该可以正常工作。我应该澄清所有这些代码都在$(document).ready(function() { 内。要设置全局变量,我应该在 document.ready 之前声明它吗?因此:

var jsonIssues = {};

$(document).ready(function() {

  var jsonIssues = {};  // declare json variable

  $.getJSON("url", function(data) {
      jsonIssues = data.Issues;
  });

  // now accessible?
}

我的印象是,在 document.ready 中声明的变量应该可以在 document.ready 的任何部分“全局”访问和修改,包括像 $.getJSON 回调函数这样的子函数。我可能需要阅读 javascript 变量范围,但似乎并不容易实现我想要的。感谢大家的回复。

更新 #2: 根据下面给出的答案,我确实使用了 $.ajax 而不是 .getJSON,并达到了我想要的结果。代码如下:

var jsonIssues = {};
    $.ajax({
        url: "url",
        async: false,
        dataType: 'json',
        success: function(data) {
            jsonIssues = data.Issues;
        }
    });

    // jsonIssues accessible here -- good!!

对我的回答进行了多次跟进(我很感激他们)。我这样做的目的是首先加载一个 JSON 对象,其中包含用户可以从中删除并保存的问题列表。但这是通过页面上的后续交互来完成的,我无法预见用户想要对回调中的 JSON 对象 within 做什么。因此需要在回调完成后使其可访问。有人在这里看到我的逻辑有缺陷吗?说真的,因为可能有一些我没有看到的东西......

另外,我正在阅读 .ajax() jQuery 文档,它说将 async 设置为 false “同步加载数据。在请求处于活动状态时阻止浏览器。最好在以下情况下通过其他方式阻止用户交互同步是必要的。”

有没有人知道在这种情况下我应该如何阻止用户交互?为什么会有这样的担忧?再次感谢所有回复。

【问题讨论】:

  • $(document).ready 只是一个在文档准备好时调用的函数。在设置 Ajax 调用(以及您正在设置的任何其他内容)之后,代码是事件驱动的——在 Ajax 调用发生之前,您的任何代码都不会运行。这就是为什么您将“所有内容”放入回调中的原因。除了回调,您的代码都没有执行。
  • 全局可以,但只有在 JSON 发生后才有效。这就是您从回调中访问它的原因。回调可以调用其他函数——如果你担心的话,你不必把所有代码都塞进去。
  • 查看我提到的解决方案示例的更新答案。
  • 通过在 ajax 调用期间不让他们做任何事情来阻止用户交互。例如,如果您有一些按钮可以让它们对加载的 Ajax 信息执行某些操作,则在加载数据之前不要显示按钮。在极端情况下,在所有内容上添加一个 div 以捕捉点击并显示 ajax 抖动。
  • 您的后续问题似乎与您最初的问题大相径庭,值得提出自己的 SO 问题。 (它似乎也很不错。)

标签: javascript jquery variables scope callback


【解决方案1】:

$.getJSON 是异步的。即调用后的代码在$.getJSON获取并解析数据并调用你的回调时执行。

所以,鉴于此:

a();

$.getJSON("url", function() {
    b();
});

c();

abc 的调用顺序可能是 a b c(在这种情况下是想要的)或 a c b(更有可能实际发生)。

解决方案?

同步 XHR 请求

使请求同步而不是异步:

a();

$.ajax({
    async: false,
    url: "url",
    success: function() {
        b();
    }
});

c();

重构代码

在调用b之后将调用移至c

a();

$.getJSON("url", function() {
    b();

    c();
});

【讨论】:

  • 感谢 strager 的快速回复。你得原谅我……即使有计算机科学学位,我仍然对这些东西有点菜鸟。我想我已经看到您可以在 $.ajax(..) 中将 async 设置为 false?我可以说“$.ajax({async: false});”吗?此外,也许更重要的是,一旦关闭异步,这意味着什么?一切都从上到下执行?再次感谢..
  • 一个合理的答案。不幸的是(正如 OP 在问题更新中发现的那样),$.getJSON 没有为 asynch 选项提供“选项覆盖”。 @Mega Matt:您必须调用$.ajax 而不是 $.getJSON,传递dataType:"json" 才能执行与getJSON 相同的工作。
  • 没有充分的理由同步而不是异步。只需将代码放入回调中即可。
  • 糟糕,我的意思是async,而不是asynch。想到的另一个选项是通过$.ajaxSettings.async = false 设置“全局”覆盖。请注意,这会影响所有 XMLHttpRequest。
  • 我的意思并不总是,@strager,我的意思是在 JavaScript Ajax 程序中。 $document ready 用于设置诸如点击处理程序之类的东西。在 JavaScript 中使用同步而不是异步是一个坏主意,因为 JS 是单线程的,线程无法结束。界面出现问题,一些浏览器会发出关于脚本未结束的警报。由于学习 Ajax 的目标通常是创建响应式网页或 RIA(不是命令行应用程序),因此需要学习异步。
【解决方案2】:

请记住,当您提供回调函数时,其目的是将回调的执行推迟到稍后执行,并立即继续执行接下来的任何内容。这是必要的,因为浏览器中 JavaScript 的单线程执行模型。强制同步执行是可能的,但它会在整个操作期间挂起浏览器。对于像 $.getJSON 这样的东西,浏览器停止响应的时间太长了。

换句话说,您正试图找到一种方法来使用这种程序范式:

var foo = {};

$.getJSON("url", function(data) {
  foo = data.property;
});

// Use foo here.

当您需要重构代码以使其更像这样流动时:

$.getJSON("url", function(data) {
  // Do something with data.property here.
});

如果您想保持回调函数简单,“做某事”可能是对另一个函数的调用。重要的是,您要等到 $.getJSON 完成后再执行代码。

您甚至可以使用自定义事件,以便在 $.getJSON 之后放置的代码订阅 IssuesReceived 事件,并在 $.getJSON 回调中引发该事件:

$(document).ready(function() {
  $(document).bind('IssuesReceived', IssuesReceived)

  $.getJSON("url", function(data) {
    $(document).trigger('IssuesReceived', data);
  });
});

function IssuesReceived(evt, data) {
  // Do something with data here.
}

更新:

或者,您可以全局存储数据,并使用自定义事件来通知已收到数据并更新全局变量。

$(document).ready(function() {
  $(document).bind('IssuesReceived', IssuesReceived)

  $.getJSON("url", function(data) {
    // I prefer the window.data syntax so that it's obvious
    //  that the variable is global.
    window.data = data;

    $(document).trigger('IssuesReceived');
  });
});

function IssuesReceived(evt) {
  // Do something with window.data here.
  //  (e.g. create the drag 'n drop interface)
}

// Wired up as the "drop" callback handler on 
//  your drag 'n drop UI.
function OnDrop(evt) {
  // Modify window.data accordingly.
}

// Maybe wired up as the click handler for a
//  "Save changes" button.
function SaveChanges() {
  $.post("SaveUrl", window.data);
}

更新 2:

对此的回应:

有没有人知道在这种情况下我应该如何阻止用户交互?为什么会有这样的担忧?再次感谢所有回复。

您应该避免使用同步 AJAX 调用阻塞浏览器的原因是,阻塞的 JavaScript 线程也会阻塞浏览器中的所有其他内容,包括其他选项卡甚至其他窗口。这意味着没有滚动,没有导航,什么都没有。出于所有意图和目的,浏览器似乎已经崩溃。正如您可以想象的那样,以这种方式运行的页面对其用户来说是一个严重的麻烦。

【讨论】:

  • 我从没想过要使用自定义事件。我将努力做到这一点。很好的答案。
  • 感谢戴夫的回答。所以我想由你运行一个场景。比方说,在 $.getJSON 回调中,我获取从调用中获得的数据,并加载一个 jquery 拖放列表,然后用户可以从该列表中删除问题(如果他选择的话)。用户不希望分配给他的特定问题,并“丢弃”它。然后他想保存分配给他的那个列表。在这里,我想更新该信息,JSON 对象将是完美的。但我不再有权访问该信息,因为回调已完成执行。我没有可以从中删除问题的列表!我该如何处理?
  • @Nosredna:自定义事件未得到充分利用,IMO。它们非常适合清理内联事件处理程序倾向于产生的嵌套匿名函数意大利面条。
  • 戴夫,我认为您的回答可能违反了我原来的异步调用问题。全局变量似乎是一个很好的解决方案,但前提是我可以确保在 $.getJSON 运行之后 调用它。如果我需要能够在回调完成后操作数据,听起来我唯一可以存储该数据的是回调范围之外的变量(并且在我需要的范围内)。并且唯一的方法是确保变量的值被设置为将ajax 设置为同步。好结论?
  • @Matt:看看我最近的编辑,包括 OnDrop 和 SaveChanges 函数,看看这是否有意义。对于事件驱动的代码,想法是根据需要对这些事件采取行动,而不是编排某个代码链以同步顺序执行所有操作。当您在单线程浏览器环境中工作时,其他任何事情都会违背规律;尤其是在使用像 jQuery 这样的事件驱动框架时。
【解决方案3】:

也许这项工作,对我有用.. :)

$variable= new array();

$.getJSON("url", function(data){
asignVariable(data);
}

function asignVariable(data){
$variable = data;
}

console.log($variable);

希望对你有帮助.. :)

【讨论】:

    【解决方案4】:

    你可以通过承诺来解决这个问题:

    var jsonPromise = $.getJSON("url")
    
    jsonPromise.done(function(data) {
        // success
        // do stuff with data
    });
    
    jsonPromise.fail(function(reason) {
        // it failed... handle it
    });
    
    // other stuff ....
    
    jsonPromise.then(function(data) {
        // do moar stuff with data
        // will perhaps fire instantly, since the deferred may already be resolved.
    });
    

    这是一种让异步代码更有必要性的非常简单且可行的方法。

    【讨论】:

      【解决方案5】:

      “但是这是通过页面上的后续交互来完成的,我无法预见用户想要在回调中对 JSON 对象做什么。”

      回调是您为用户与数据交互设置屏幕的机会。

      您可以为用户创建或显示 HTML,并设置更多回调。

      大多数时候,您的代码都不会运行。编写 Ajax 页面就是要考虑哪些事件可能在何时发生。

      它是“Ajax”而不是“Sjax”是有原因的。从异步更改为同步很痛苦是有原因的。预计您将执行页面异步操作。

      事件驱动的编程一开始可能会令人沮丧。

      我在 JS 中完成了计算密集型金融算法。即使那样,它也是一样的——你把它分成小部分,事件就是超时。

      JavaScript 中的动画也是事件驱动的。事实上,浏览器甚至不会显示移动,除非您的脚本反复放弃控制。

      【讨论】:

      • 您已经说服我同步 ajax 不是可行的方法,但那时我仍在努力应对流程。请参阅我对 Dave Ward 回答的后续评论。
      【解决方案6】:

      您只是遇到了范围界定问题。

      简答:

      window.jsonIssues = {};  // or tack on to some other accessible var
      
      $.getJSON("url", function(data) {
          window.jsonIssues = data.Issues;
      });
      
      // see results here
      alert(window.jsonIssues);
      

      长答案:

      Scoping issue in Javascript Javascript closure scoping issue

      【讨论】:

      • 除非我使用“窗口”误解了您的简短回答。前面的变量不起作用。您只是想确定一个范围吗?
      • 不,这不是范围问题,这是一个异步问题。在一切前面加上window. 绝不会使异步操作同步。您是否阅读了问题?
      • @megamatt:你说得对,里面有一个无关的“var”; @crescent:最初的问题没有问任何关于异步的问题,只是在我发布后的编辑添加了特定的异步点。
      猜你喜欢
      • 1970-01-01
      • 2018-12-02
      • 1970-01-01
      • 1970-01-01
      • 2015-04-29
      • 1970-01-01
      • 2017-02-02
      • 2011-05-12
      相关资源
      最近更新 更多