Como detectar quando o FB.init do Facebook está completo

115

O antigo JS SDK tinha uma função chamada FB.ensureInit. O novo SDK parece não ter essa função ... como posso garantir que não faço chamadas de API até que seja totalmente iniciado?

Incluo no topo de cada página:

<div id="fb-root"></div>
<script>
  window.fbAsyncInit = function() {
    FB.init({
      appId  : '<?php echo $conf['fb']['appid']; ?>',
      status : true, // check login status
      cookie : true, // enable cookies to allow the server to access the session
      xfbml  : true  // parse XFBML
    });
    FB.Canvas.setAutoResize();
  };

  (function() {
    var e = document.createElement('script');
    e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
    e.async = true;
    document.getElementById('fb-root').appendChild(e);
  }());
</script>
Pablo
fonte
Veja este link para minha solução para este problema. facebook.stackoverflow.com/questions/12399428/…
Remi Grumeau

Respostas:

138

Atualização em 04 de janeiro de 2012

Parece que você não pode simplesmente chamar métodos dependentes de FB (por exemplo FB.getAuthResponse()) logo depois FB.init()como antes, pois FB.init()parece ser assíncrono agora. Encapsular seu código em uma FB.getLoginStatus()resposta parece funcionar para detectar quando a API está totalmente pronta:

window.fbAsyncInit = function() {
    FB.init({
        //...
    });

    FB.getLoginStatus(function(response){
        runFbInitCriticalCode(); 
    });

};  

ou se estiver usando a fbEnsureInit()implementação abaixo:

window.fbAsyncInit = function() {
    FB.init({
        //...
    });

    FB.getLoginStatus(function(response){
        fbApiInit = true;
    });

};  

Postagem original:

Se você quiser apenas executar algum script quando o FB for inicializado, você pode colocar alguma função de retorno de chamada dentro fbAsyncInit:

  window.fbAsyncInit = function() {
    FB.init({
      appId  : '<?php echo $conf['fb']['appid']; ?>',
      status : true, // check login status
      cookie : true, // enable cookies to allow the server to access the session
      xfbml  : true  // parse XFBML
    });
    FB.Canvas.setAutoResize();

    runFbInitCriticalCode(); //function that contains FB init critical code
  };

Se você deseja uma substituição exata de FB.ensureInit, então você terá que escrever algo por conta própria, pois não há uma substituição oficial (grande erro, imo). Aqui está o que eu uso:

  window.fbAsyncInit = function() {
    FB.init({
      appId  : '<?php echo $conf['fb']['appid']; ?>',
      status : true, // check login status
      cookie : true, // enable cookies to allow the server to access the session
      xfbml  : true  // parse XFBML
    });
    FB.Canvas.setAutoResize();

    fbApiInit = true; //init flag
  };

  function fbEnsureInit(callback) {
        if(!window.fbApiInit) {
            setTimeout(function() {fbEnsureInit(callback);}, 50);
        } else {
            if(callback) {
                callback();
            }
        }
    }

Uso:

fbEnsureInit(function() {
    console.log("this will be run once FB is initialized");
});
serg
fonte
2
Obrigada Serg .. você é uma lenda!
Pablo
3
Não consigo executar uma solicitação de API do Facebook imediatamente após FB.init ser chamado - talvez porque o método que estou usando seja 'fql.query'. De qualquer forma, preciso definir um longo tempo limite no sinalizador fbApiInit.
Ian Hunter
Obrigado pela atualização - Eu estava tendo o mesmo problema e sua solução atualizada funcionou perfeitamente!
Terri Ann
@beanland, chamadas de api fql.query exigem que o usuário tenha efetuado login. Tem certeza de que está executando a consulta após obter uma responseresposta FB.getLoginStatus(function(response){}? - observe a diferença entre a "resposta atualizada" e a "resposta original" acima.
tjmehta
@serg obrigado pelo exemplo. Tive que colocar um atraso na minha função interna fbAsyncInitpara que ela fosse chamada após o carregamentowindow.fbAsyncInit = function() { FB.init({ appId : '*************', channelUrl : 'http://www.mydomain.com', status : true, // check login status cookie : true, // enable cookies to allow the server to access the session oauth : true, // enable OAuth 2.0 xfbml : true // parse XFBML }); setTimeout(function() { myfunction(function (callback) {}, '', '', '' ); }, 1200); };
tq
38

Na verdade, o Facebook já forneceu um mecanismo para se inscrever em eventos de autenticação.

No seu caso, você está usando " status: true ", o que significa que o objeto FB solicitará ao Facebook o status de login do usuário.

FB.init({
    appId  : '<?php echo $conf['fb']['appid']; ?>',
    status : true, // check login status
    cookie : true, // enable cookies to allow the server to access the session
    xfbml  : true  // parse XFBML
});

Ao chamar "FB.getLoginStatus ()", você está executando a mesma solicitação novamente .

Em vez disso, você poderia usar FB.Event.subscribe subscrever a auth.statusChange ou auth.authResponseChange evento ANTES você ligar FB.init

FB.Event.subscribe('auth.statusChange', function(response) {
    if(response.status == 'connected') {
        runFbInitCriticalCode();
    }
});

FB.init({
    appId  : '<?php echo $conf['fb']['appid']; ?>',
    status : true, // check login status
    cookie : true, // enable cookies to allow the server to access the session
    xfbml  : true  // parse XFBML
});

Provavelmente, ao usar " status: false ", você pode executar qualquer código logo após FB.init, porque não haverá chamadas assíncronas.

shpoont
fonte
NOTA: Com este método, a única ressalva é que, se você não estiver conectado, o evento não será acionado. Basta definir o estado da interface do usuário padrão como se não estivesse autenticado e funcionará perfeitamente. Você pode então verificar se há conectado ou not_authorized dentro do manipulador de eventos.
David C
Além disso, para aplicativos front-end, para manter o status de login do usuário entre as atualizações, defina cookiecomo trueno objeto init param.
MK Safi,
12

Aqui está uma solução caso você use e carregamento lento assíncrono do Facebook:

// listen to an Event
$(document).bind('fbInit',function(){
    console.log('fbInit complete; FB Object is Available');
});

// FB Async
window.fbAsyncInit = function() {
    FB.init({appId: 'app_id', 
         status: true, 
         cookie: true,
         oauth:true,
         xfbml: true});

    $(document).trigger('fbInit'); // trigger event
};
vladaman
fonte
3
Eu amo isso! O único problema é que o Facebook insiste que você deve incluir seu código init do FB diretamente após a tag de abertura <body>, e a prática recomendada diz que você deve carregar o jQuery antes da tag de fechamento <body>. Doh! Frango e ovo. Estou tentado a dizer "dane-se, Facebook" e carregar o FB após o jQuery.
Ethan Brown
@ EthanBrown Eu concordo - sua página provavelmente precisa de jQuery mais do que de FB, então isso faz sentido.
Desisti de
adicione version: 'v2.5'no init json de outro modo não funcionará .. pelo menos não funcionou para mim. obrigado pela ajuda
Vikas Bansal
10

Outra maneira de verificar se o FB foi inicializado é usando o seguinte código:

ns.FBInitialized = function () {
    return typeof (FB) != 'undefined' && window.fbAsyncInit.hasRun;
};

Portanto, em seu evento de página pronta, você pode marcar ns.FBInitialized e adiar o evento para uma fase posterior usando setTimeOut.

user2253630
fonte
Isso é real! Obrigado!
Thiago Macedo
Este é simples. Gostar
Faris Rayhan
5

Embora algumas das soluções acima funcionem, pensei em postar nossa solução eventual - que define um método 'pronto' que será acionado assim que o FB for inicializado e estiver pronto para funcionar. Ele tem a vantagem sobre outras soluções de que é seguro ligar antes ou depois do FB estar pronto.

Pode ser usado da seguinte forma:

f52.fb.ready(function() {
    // safe to use FB here
});

Aqui está o arquivo de origem (observe que ele está definido dentro de um namespace 'f52.fb').

if (typeof(f52) === 'undefined') { f52 = {}; }
f52.fb = (function () {

    var fbAppId = f52.inputs.base.fbAppId,
        fbApiInit = false;

    var awaitingReady = [];

    var notifyQ = function() {
        var i = 0,
            l = awaitingReady.length;
        for(i = 0; i < l; i++) {
            awaitingReady[i]();
        }
    };

    var ready = function(cb) {
        if (fbApiInit) {
            cb();
        } else {
            awaitingReady.push(cb);
        }
    };

    window.fbAsyncInit = function() {
        FB.init({
            appId: fbAppId,
            xfbml: true,
            version: 'v2.0'
        });

        FB.getLoginStatus(function(response){
            fbApiInit = true;
            notifyQ();
        });
    };

    return {
        /**
         * Fires callback when FB is initialized and ready for api calls.
         */
        'ready': ready
    };

})();
Karl Rosaen
fonte
4

Evitei usar setTimeout usando uma função global:

NOTA EDITAR: Eu atualizei os seguintes scripts auxiliares e criei uma classe mais fácil / simples de usar; confira aqui ::: https://github.com/tjmehta/fbExec.js

window.fbAsyncInit = function() {
    FB.init({
        //...
    });
    window.fbApiInit = true; //init flag
    if(window.thisFunctionIsCalledAfterFbInit)
        window.thisFunctionIsCalledAfterFbInit();
};

fbEnsureInit chamará seu retorno de chamada após FB.init

function fbEnsureInit(callback){
  if(!window.fbApiInit) {
    window.thisFunctionIsCalledAfterFbInit = callback; //find this in index.html
  }
  else{
    callback();
  }
}

fbEnsureInitAndLoginStatus chamará seu retorno de chamada após FB.init e após FB.getLoginStatus

function fbEnsureInitAndLoginStatus(callback){
  runAfterFbInit(function(){
    FB.getLoginStatus(function(response){
      if (response.status === 'connected') {
        // the user is logged in and has authenticated your
        // app, and response.authResponse supplies
        // the user's ID, a valid access token, a signed
        // request, and the time the access token
        // and signed request each expire
        callback();

      } else if (response.status === 'not_authorized') {
        // the user is logged in to Facebook,
        // but has not authenticated your app

      } else {
        // the user isn't logged in to Facebook.

      }
    });
  });
}

Exemplo de uso de fbEnsureInit:

(FB.login precisa ser executado após o FB ter sido inicializado)

fbEnsureInit(function(){
    FB.login(
       //..enter code here
    );
});

Exemplo de uso de fbEnsureInitAndLogin:

(FB.api precisa ser executado após FB.init e o usuário FB deve estar conectado.)

fbEnsureInitAndLoginStatus(function(){
    FB.api(
       //..enter code here
    );
});
tjmehta
fonte
1
Eu escrevi esta classe auxiliar que tem o mesmo desempenho acima de uma maneira mais fácil e limpa; confira aqui :: github.com/tjmehta/fbExec.js
tjmehta
4

Em vez de usar qualquer setTimeout ou setInterval, preferiria objetos adiados (implementação por jQuery aqui ). Ainda é complicado resolver a fila no momento adequado, porque o init não tem retornos de chamada, mas combinando resultado com inscrição de evento (como alguém apontou antes de mim), deve resolver o problema e estar próximo o suficiente.

O pseudo-snippet seria o seguinte:

FB.Event.subscribe('auth.statusChange', function(response) {
   if (response.authResponse) {
       // user has auth'd your app and is logged into Facebook
   } else {
       // user has not auth'd your app, or is not logged into Facebook
   }
   DeferredObject.resolve();
});
Drachenfels
fonte
4

Aqui está um método mais simples, que não requer eventos ou tempos limite. No entanto, requer jQuery.

Use jQuery.holdReady() (docs)

Portanto, imediatamente após seu script jQuery, atrase o evento pronto.

<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
    $.holdReady( true ); // don't fire ready until told (ie when FB loaded)
</script>

Então, em sua função de inicialização do Facebook, libere-o:

window.fbAsyncInit = function() {
    FB.init({
        appId: '11111111111111',
        cookie: true,
        xfbml: false,
        version: 'v2.4'
    });

    // release the ready event to execute
    $.holdReady( false );
};

Então você pode usar o evento pronto normalmente:

$(document).ready( myApp.init );
voidstate
fonte
2

Você pode se inscrever no evento:

ie)

FB.Event.subscribe('auth.login', function(response) {
  FB.api('/me', function(response) {
    alert(response.name);
  });
});
Jonathan Tonge
fonte
2

Avisos pequenos, mas IMPORTANTES:

  1. FB.getLoginStatusdeve ser chamado depois FB.init, caso contrário, não disparará o evento.

  2. você pode usar FB.Event.subscribe('auth.statusChange', callback), mas ele não irá disparar quando o usuário não estiver logado no Facebook.

Aqui está o exemplo de trabalho com ambas as funções

window.fbAsyncInit = function() {
    FB.Event.subscribe('auth.statusChange', function(response) {
        console.log( "FB.Event.subscribe auth.statusChange" );
        console.log( response );
    });

    FB.init({
        appId   : "YOUR APP KEY HERE",
        cookie  : true,  // enable cookies to allow the server to access
                // the session
        xfbml   : true,  // parse social plugins on this page
        version : 'v2.1', // use version 2.1
        status  : true
    });

    FB.getLoginStatus(function(response){
        console.log( "FB.getLoginStatus" );
        console.log( response );
    });

};

// Load the SDK asynchronously
(function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
Mher Aghabalyan
fonte
1

A API do Facebook monitora o FB._apiKey para que você possa observar isso antes de chamar seu próprio aplicativo da API com algo como:

window.fbAsyncInit = function() {
  FB.init({
    //...your init object
  });
  function myUseOfFB(){
    //...your FB API calls
  };
  function FBreadyState(){
    if(FB._apiKey) return myUseOfFB();
    setTimeout(FBreadyState, 100); // adjust time as-desired
  };
  FBreadyState();
}; 

Não tenho certeza se isso faz diferença, mas no meu caso - porque eu queria ter certeza de que a IU estava pronta - eu envolvi a inicialização com o documento jQuery pronto (último pedaço acima):

  $(document).ready(FBreadyState);

Observe também que NÃO estou usando async = true para carregar o all.js do Facebook, o que, no meu caso, parece estar ajudando a acessar a IU e a conduzir os recursos de maneira mais confiável.

Jimmont
fonte
1

Às vezes, fbAsyncInit não funciona. Não sei por que e uso esta solução alternativa então:

 var interval = window.setInterval(function(){
    if(typeof FB != 'undefined'){
        FB.init({
            appId      : 'your ID',
            cookie     : true,  // enable cookies to allow the server to access// the session
            xfbml      : true,  // parse social plugins on this page
            version    : 'v2.3' // use version 2.3
        });

        FB.getLoginStatus(function(response) {
            statusChangeCallback(response);
        });
        clearInterval(interval);
    }
},100);
pax
fonte