Come rilevare quando FB.init di Facebook è completo


115

Il vecchio JS SDK aveva una funzione chiamata FB.ensureInit. Il nuovo SDK non sembra avere tale funzione ... come posso assicurarmi di non effettuare chiamate api fino a quando non viene completamente avviato?

Includo questo nella parte superiore di ogni pagina:

<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>

Vedi questo link per la mia soluzione a questo problema. facebook.stackoverflow.com/questions/12399428/…
Remi Grumeau

Risposte:


138

Aggiornamento il 4 gennaio 2012

Sembra che non sia possibile chiamare metodi dipendenti da FB (ad esempio FB.getAuthResponse()) subito dopo FB.init()come prima, poiché ora FB.init()sembra essere asincrono. Il wrapping del codice in FB.getLoginStatus()risposta sembra fare il trucco per rilevare quando l'API è completamente pronta:

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

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

};  

o se si utilizza l' fbEnsureInit()implementazione dal basso:

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

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

};  

Post originale:

Se vuoi solo eseguire uno script quando FB è inizializzato, puoi inserire alcune funzioni di callback all'interno 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 vuoi la sostituzione esatta di FB.ensureInit, dovresti scrivere qualcosa da solo poiché non esiste una sostituzione ufficiale (grande errore imo). Ecco cosa 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");
});

2
Grazie Serg .. sei una leggenda!
Pablo

3
Non sono in grado di eseguire una richiesta API di Facebook immediatamente dopo la chiamata a FB.init, forse perché il metodo che sto utilizzando è "fql.query"? Ad ogni modo, devo impostare un timeout lungo sul flag fbApiInit.
Ian Hunter

Grazie per l'aggiornamento: avevo lo stesso problema e la tua soluzione aggiornata ha funzionato perfettamente!
Terri Ann

Le chiamate api @beanland, fql.query richiedono che l'utente abbia effettuato l'accesso. Sei sicuro di eseguire la query dopo aver ricevuto una responserisposta da FB.getLoginStatus(function(response){}? - nota la differenza tra la "risposta aggiornata" e la "risposta originale" sopra.
tjmehta

@serg grazie per l'esempio. Ho dovuto mettere un ritardo sulla mia funzione all'interno fbAsyncInitperché fosse chiamata dopo il caricamentowindow.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

In realtà Facebook ha già fornito un meccanismo per iscriversi agli eventi di autenticazione.

Nel tuo caso stai usando " status: true ", il che significa che l'oggetto FB richiederà a Facebook lo stato di accesso dell'utente.

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
});

Chiamando "FB.getLoginStatus ()" si esegue nuovamente la stessa richiesta .

Invece si potrebbe usare FB.Event.subscribe di sottoscrivere auth.statusChange o auth.authResponseChange evento PRIMA si chiama 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
});

Molto probabilmente, quando si utilizza " status: false " è possibile eseguire qualsiasi codice subito dopo FB.init, poiché non ci saranno chiamate asincrone.


NOTA: con questo metodo l'unico avvertimento è che, se non sei loggato, l'evento non verrà attivato. Basta impostare lo stato dell'interfaccia utente predefinita come se non fosse autenticato e funziona perfettamente. È quindi possibile verificare la presenza di connected o not_authorized all'interno del gestore eventi.
David C

Inoltre, per le app front-end, per mantenere lo stato di accesso dell'utente tra gli aggiornamenti, impostato cookiesu truenell'oggetto init param.
MK Safi

12

Ecco una soluzione nel caso in cui utilizzi e caricamento lento asincrono di 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
};

3
Lo adoro! L'unico problema è che Facebook insiste sul fatto che dovresti includere il tuo codice di inizializzazione FB direttamente dopo il tag di apertura <body> e la best practice dice che dovresti caricare jQuery prima del tag di chiusura <body>. Doh! Pollo e uova. Sono tentato di dire "fottiti, Facebook" e caricare FB dopo jQuery.
Ethan Brown

@ EthanBrown Sono d'accordo - la tua pagina probabilmente ha bisogno di jQuery più di quanto abbia bisogno di FB, quindi ha senso. Ho rinunciato a caricare jQuery alla fine della pagina anni fa ;-)
Simon_Weaver

aggiungere version: 'v2.5'l'init json in altro modo non funzionerà .. almeno non ha fatto per me. grazie per l'aiuto
Vikas Bansal

10

Un altro modo per verificare se FB è stato inizializzato è utilizzare il codice seguente:

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

Quindi nel tuo evento di pagina pronta puoi controllare ns.FBInitialized e rimandare l'evento a una fase successiva utilizzando setTimeOut.


Questa è la cosa reale! Grazie!
Thiago Macedo

Questo è semplice. Mi piace
Faris Rayhan

5

Mentre alcune delle soluzioni di cui sopra funzionano, ho pensato di pubblicare la nostra eventuale soluzione, che definisce un metodo "pronto" che verrà attivato non appena FB viene inizializzato e pronto per l'uso. Ha il vantaggio rispetto ad altre soluzioni che è sicuro chiamare prima o dopo che FB è pronto.

Può essere usato in questo modo:

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

Ecco il file sorgente (nota che è definito all'interno di uno spazio dei nomi "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
    };

})();

4

Ho evitato di usare setTimeout usando una funzione globale:

NOTA EDIT: ho aggiornato i seguenti script di supporto e creato una classe più facile / semplice da usare; dai un'occhiata qui ::: https://github.com/tjmehta/fbExec.js

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

fbEnsureInit chiamerà la sua richiamata dopo FB.init

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

fbEnsureInitAndLoginStatus chiamerà il suo callback dopo FB.init e dopo 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.

      }
    });
  });
}

fbEnsureInit esempio di utilizzo:

(FB.login deve essere eseguito dopo che FB è stato inizializzato)

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

Esempio di utilizzo di fbEnsureInitAndLogin:

(FB.api deve essere eseguito dopo FB.init e l'utente FB deve essere loggato.)

fbEnsureInitAndLoginStatus(function(){
    FB.api(
       //..enter code here
    );
});

1
Ho scritto questa classe di supporto che esegue le stesse operazioni di cui sopra in un modo più semplice e pulito; dai un'occhiata qui :: github.com/tjmehta/fbExec.js
tjmehta

4

Invece di usare qualsiasi setTimeout o setInterval, mi atterrei a oggetti differiti (implementazione di jQuery qui ). È ancora difficile risolvere la coda al momento giusto, perché init non ha callback ma combinando il risultato con la sottoscrizione dell'evento (come qualcuno mi ha indicato prima), dovrebbe fare il trucco ed essere abbastanza vicino.

Lo pseudo-snippet dovrebbe apparire come segue:

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();
});

4

Ecco un metodo più semplice, che non richiede né eventi né timeout. Tuttavia, richiede jQuery.

Usa jQuery.holdReady() (documenti)

Quindi, subito dopo lo script jQuery, ritarda l'evento ready.

<!-- 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>

Quindi, nella tua funzione di inizializzazione di Facebook, rilascialo:

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

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

Quindi puoi usare l'evento ready come di consueto:

$(document).ready( myApp.init );

2

Puoi iscriverti all'evento:

ie)

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

2

Avvisi piccoli ma IMPORTANTI:

  1. FB.getLoginStatusdeve essere chiamato dopo FB.init, altrimenti l'evento non verrà attivato.

  2. puoi usare FB.Event.subscribe('auth.statusChange', callback), ma non si attiverà quando l'utente non è connesso a Facebook.

Ecco l'esempio funzionante con entrambe le funzioni

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'));

1

L'API di Facebook controlla la FB._apiKey in modo che tu possa controllarla prima di chiamare la tua applicazione dell'API con qualcosa del tipo:

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();
}; 

Non sono sicuro che questo faccia la differenza, ma nel mio caso - perché volevo essere sicuro che l'interfaccia utente fosse pronta - ho avvolto l'inizializzazione con il documento di jQuery pronto (ultimo bit sopra):

  $(document).ready(FBreadyState);

Nota anche che NON sto usando async = true per caricare il file all.js di Facebook, che nel mio caso sembra aiutare con l'accesso all'interfaccia utente e le funzioni di guida in modo più affidabile.


1

A volte fbAsyncInit non funziona. Non so perché e allora uso questa soluzione alternativa:

 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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.