在JavaScript中等待API调用完成后再继续操作

12

我过去一直在努力解决的问题,今天仍然无法解决的是,在收到响应之前阻止API/AJAX继续。目前我正在使用Facebook API。我需要从调用中获取响应,然后返回它,但我的函数在我从API调用中获得响应之前就已经返回了。我知道为什么会发生这种情况,但我无法找出如何防止它!这是我的代码...

function makeCall(){

var finalresponse = "";
    var body = 'Test post';

      FB.api('/me/feed', 'post', { message: body }, function(response) {
        if (!response || response.error) {
         finalresponse = response.error;
        } else {
          finalresponse = 'Post ID: ' + response.id;
        }
      });
      return finalresponse;

}

// ----- 编辑

我注意到有些人建议这样做...

function makeCall(){
var finalresponse = "";
FB.api('/me/feed', 'post', { message: body }, function(response) {
        if (!response || response.error) {
         finalresponse = response.error;
         return finalresponse;
        } else {
          finalresponse = 'Post ID: ' + response.id;
          return finalresponse;
        }
      });
}

但是这会返回未定义

// 根据更新进行编辑

function share_share(action_id){

  var finalresponse = makeCall(action_id, process);
  return finalresponse;

}

function makeCall(action_id, callback){

var body = 'Test post';
      FB.api('/me/feed', 'post', { message: body }, function (response) {
        if (!response || response.error) {
         var finalresponse = response.error;
        } else {
          finalresponse = 'Post ID: ' + response.id;
        }
        callback(action_id, finalresponse);
      });

}
function process(action_id, finalresponse){
  console.log(finalresponse);
}

我认为是因为你的 return finalresponse; 在 API 调用之外。 - ayyp
您的返回将在FB.api完成之前执行。 - Dhiraj
您提供的编辑代码是不正确的。在回调中返回finalResponse将返回给响应回调的调用者。makeCall()返回undefined的原因是makeCall未返回任何内容。 - Nadir Muzaffar
3个回答

26

这个问题每天被问100次,似乎不可能有一个确定的答案。

由于调用是异步的,所以不可能一步完成。这是你期望的基本示例。

function one() {
  var a = 1;
  return a;
}
alert( one() );

实际上正在发生什么:

function one() {
  var a;
  window.setTimeout( 
     function() {
        a = 1;
     }, 2000);
  return a;  //This line does not wait for the asynchronous call [setTimeout/ajax call] to run. It just goes!
}
alert( one() );

你需要做的是将其分成两部分。

function one( callback ) {   
   window.setTimeout( function(){  //acting like this is an Ajax call
        var a = 1;
        callback(a);
   },2000);
}
function two( response ) {
   alert(response);
}
one( two );

所以在您的情况下,您需要将您的代码分成两个块来处理它。

function makeCall( callback ) {
    var body = 'Test post';
      FB.api('/me/feed', 'post', { message: body }, function (response) {
        if (!response || response.error) {
         var finalresponse = response.error;
        } else {
          finalresponse = 'Post ID: ' + response.id;
        }
        callback(finalresponse);
      });
}


function processResponse( response ) {
    console.log(response);
}
makeCall(processResponse);

谢谢,这真的帮助我理解了,但如果我想将响应返回给原始调用怎么办?请参见我上面的最后一次编辑。我想获取该值并返回它。因此,我有一个调用share_share的函数,需要通过响应进行返回。 - G.Thompson
不能直接返回,必须使用回调函数。return finalresponse;与之前的代码相同,只是用了不同的方式! - epascarello
1
那么我真的不能做我想做的事情吗?无法将ajax/api响应返回给父函数吗? - G.Thompson
1
如果可以使用同步调用完成,但是你正在调用另一个域,因此必须使用异步方式。你需要将逻辑分解为两个步骤。 - epascarello

7
JavaScript中没有等待或者让出控制的概念。JavaScript会一直执行你的代码,直到执行完毕。虽然这听起来有些奇怪和麻烦,但它也有其优点。
因此,在这种情况下,你想要在接收响应后执行的代码应该放在你传递给FB.api()的回调函数中。你需要将return语句后面的代码移到响应的回调函数中,这样它才能在接收到响应时被执行。
如果JavaScript像大多数其他语言(如C++/Java)那样,以下是你可能期望的:
var futureResult = SomeAsyncCall();
futureResult.Wait(); //wait untill SomeAsyncCall has returned
var data = futureResult.GetData();
//now do stuff with data

在处理异步操作时,JavaScript的思想是基于回调函数的:
SomeAsyncCall(function(result) {
    var data = result.GetData();
    //now do stuff with data
});

感谢您的时间,您有什么建议来完成这个任务吗? - G.Thompson
我添加了一个例子来阐明这个想法。 - Nadir Muzaffar
1
如果有很多无法放入回调函数的级联代码怎么办? - fireball.1

3

您不希望防止返回它,因为这样做会在请求期间阻止用户的浏览器。如果请求由于拥塞、网站维护等原因而挂起,则浏览器将对任何用户输入无响应,导致用户感到不满。

不要按照以下方式操作:

var res = makeCall();
if(res)
{
   // do stuff
}

请执行以下操作:

function makeCall(){
    FB.api('/me/feed', 'post', { message: body }, function(response) {
        if (response && !response.error) {
            // do stuff
        }
    });
}

makeCall();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接