如何让 getJSON 在 运行 其余代码之前等待?

How to make the getJSON wait before running the rest of the code?

我写了一个简单的函数来确定你的城市位置。但是,我想知道是否有办法强制函数等待 getJSON 然后移动到下一行代码?在示例中,您会看到它显示 "Alert 1" 跳过 "Alert 2" 并直接转到 "Alert 3",然后显示 "Alert 2"。提前谢谢你。

此致, 乔治

var Alpha;

function Location(){
 alert(Alpha + " 1");
 $.getJSON("http://freegeoip.net/json/?callback=?", function(location){
  Alpha = location.city;
  alert(Alpha + " 2");
 }); 
 alert(Alpha + " 3");
}

$(document).ready(function() {
 Location();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

无法让调用 getJSON 的函数等待。这样做有悖于 JavaScript 用于异步事件的模型。调用完成后要执行的任何代码都必须在其中一个回调中(或从中调用)。 (或者你可以使用一个 promise API,它在引擎盖下是完全相同的,但语法更好......)

把调用getJSON()后需要执行的逻辑放在回调函数中,这就是函数意图,像这样:

var Alpha;
function ineedtorunmorecode(){
    alert(Alpha + " 4");
}

function Location(){
    alert(Alpha + " 1");
    $.getJSON("http://freegeoip.net/json/?callback=?", function(location){
        Alpha = location.city;
        alert(Alpha + " 2");
        alert(Alpha + " 3");
        ineedtorunmorecode();
    }); 
}

$(document).ready(function() {
    Location();
});

作为异步函数不会等待,但您可以通过这样的方法清理代码:

var Alpha;

function Location(){
 alert(Alpha + " 1");

   var onSuccess = function(location) {
                Alpha = location.city;
               alert(Alpha + " 2");
    }
 $.getJSON("http://freegeoip.net/json/?callback=?", onSuccess); 
  
        
}

$(document).ready(function() {
 Location();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

你可以在$.getJson完成后指定code为运行。这是 jQuery 文档中的示例。

var jqxhr = $.getJSON( "example.json", function() {
  console.log( "success" );
  })
  .done(function() {
    console.log( "second success" );
  })
  .fail(function() {
    console.log( "error" );
  })
  .always(function() {
    console.log( "complete" );
  });

jQuery Docs

不幸的是,在 javascript 中没有简单的等待方法。你必须使用回调函数。

the documentation的例子中提到,当$.getJSONdone时,可以执行代码。在这里你可以看到这个例子。我添加了一些 console.log 来向您展示代码是如何执行的。

(function() {
  console.log("Start Snippet");
  var flickerAPI = "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?";
  console.log("Start Load");
  $.getJSON( flickerAPI, {
    tags: "mount rainier",
    tagmode: "any",
    format: "json"
  })
    .done(function( data ) {
      console.log("End Load");
      $.each( data.items, function( i, item ) {
        $( "<img>" ).attr( "src", item.media.m ).appendTo( "#images" );
        if ( i === 3 ) {
          return false;
        }
      });
    });
  console.log("End Snippet");
})();
img {
  height: 100px;
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="images"></div>

在您的代码中,异步获取 JSON return结果。这意味着代码将在执行时保持 运行。

您可以将代码移动到成功回调以确保在结果 returned 之前没有任何运行,如下所示:

var Alpha;

function Location(){
    alert(Alpha + " 1");
    $.getJSON("http://freegeoip.net/json/?callback=?", function(location){
        Alpha = location.city;
        whenDone();
    }); 
}

function whenDone() {
    alert(Alpha + " 2");
    alert(Alpha + " 3");
}

$(document).ready(function() {
    Location();
});

在较新的 JavaScript 版本中,您可以利用 async/await 和 Promises 的力量以某种方式 "wait" 执行:

let Alpha;

function Location() {
  return new Promise((resolve, reject) => {
    $.getJSON("http://freegeoip.net/json/?callback=?", location => {
      resolve(location);
    }); 
  }
}

async function init() {
  try {
    let location = await Location();
    Alpha = location.city;
  } catch(error) {
    console.error(error);
  }
}

$(document).ready(function() {
  init();
});

这将需要您对某些功能进行 polyfill。

注意:jQuery return 的较新版本是一个 Promise 兼容接口,您可以使用它来等待。我没有尝试过,但是将调用包装在 Promise 中无论如何都适用于旧版本。

getJSON() 是一个 shorthand Ajax 函数,所以, $.ajaxSetup({async : false});会成功的