Cómo detectar cuando el FB.init de Facebook está completo


115

El antiguo JS SDK tenía una función llamada FB.ensureInit. El nuevo SDK no parece tener tal función ... ¿cómo puedo asegurarme de que no hago llamadas a la API hasta que esté completamente iniciado?

Incluyo esto en la parte superior 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>

Vea este enlace para ver mi solución a este problema. facebook.stackoverflow.com/questions/12399428/…
Remi Grumeau

Respuestas:


138

Actualización el 04 de enero de 2012

Parece que no puede simplemente llamar a métodos dependientes de FB (por ejemplo FB.getAuthResponse()) justo después FB.init()como antes, ya que ahora FB.init()parece ser asíncrono. Envolver su código en FB.getLoginStatus()respuesta parece hacer el truco de detectar cuando la API está completamente lista:

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

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

};  

o si usa la fbEnsureInit()implementación desde abajo:

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

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

};  

Publicación original:

Si solo desea ejecutar algún script cuando FB se inicializa, puede poner alguna función de devolución de llamada 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
  };

Si desea un reemplazo exacto de FB.ensureInit, entonces tendrá que escribir algo por su cuenta ya que no hay un reemplazo oficial (gran error en mi opinión). Esto es lo que 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
Gracias Serg ... ¡eres una leyenda!
Pablo

3
No puedo ejecutar una solicitud de API de Facebook inmediatamente después de que se llame a FB.init, ¿quizás porque el método que estoy usando es 'fql.query'? De cualquier manera, tengo que establecer un tiempo de espera prolongado en la bandera fbApiInit.
Ian Hunter

Gracias por la actualización. Estaba teniendo el mismo problema y su solución actualizada funcionó perfectamente.
Terri Ann

@beanland, las llamadas a la API fql.query requieren que el usuario haya iniciado sesión. ¿Está seguro de que está ejecutando la consulta después de recibir una responserespuesta FB.getLoginStatus(function(response){}? - observe la diferencia entre la "respuesta actualizada" y la "respuesta original" anterior.
tjmehta

@serg gracias por el ejemplo. Tuve que poner un retraso en mi función dentro fbAsyncInitpara que se llamara después de la cargawindow.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

De hecho, Facebook ya ha proporcionado un mecanismo para suscribirse a eventos de autenticación.

En su caso, está utilizando " status: true ", lo que significa que el objeto FB solicitará a Facebook el estado de inicio de sesión del usuario.

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

Al llamar a "FB.getLoginStatus ()", vuelve a ejecutar la misma solicitud .

En su lugar, puede usar FB.Event.subscribe para suscribirse al evento auth.statusChange o auth.authResponseChange ANTES de llamar a 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
});

Lo más probable es que cuando utilice " status: false " pueda ejecutar cualquier código inmediatamente después de FB.init, porque no habrá llamadas asincrónicas.


NOTA: Con este método, la única advertencia es que, si no está conectado, el evento no se activará. Simplemente haga que su interfaz de usuario predeterminada esté como si no estuviera autenticada, y funciona perfectamente. A continuación, puede verificar si está conectado o no autorizado dentro del controlador de eventos.
David C

Además, para las aplicaciones de front-end, para mantener el estado de inicio de sesión del usuario entre actualizaciones, se establece cookieen trueen el objeto init param.
MK Safi

12

Aquí hay una solución en caso de que use y carga diferida asincrónica de 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
¡Me encanta! El único problema es que Facebook insiste en que debes incluir tu código de inicio de FB directamente después de la etiqueta <body> de apertura, y las mejores prácticas dicen que debes cargar jQuery antes de la etiqueta <body> de cierre. Doh! Huevo y gallina. Estoy tentado a decir "que te jodan, Facebook" y cargar FB después de jQuery.
Ethan Brown

@EthanBrown Estoy de acuerdo: tu página probablemente necesite más jQuery de lo que necesita FB, por lo que tiene sentido. Dejé de intentar cargar jQuery al final de la página hace años ;-)
Simon_Weaver

agregar version: 'v2.5'en el json de inicio de lo contrario no funcionará ... al menos no lo hizo para mí. gracias por la ayuda
Vikas Bansal

10

Otra forma de verificar si FB se ha inicializado es usando el siguiente código:

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

Por lo tanto, en su evento de página lista, puede verificar ns.FBInitialized y aplazar el evento a una fase posterior utilizando setTimeOut.


¡Esto es real! ¡Gracias!
Thiago Macedo

Este es simple. Me gusta
Faris Rayhan

5

Si bien algunas de las soluciones anteriores funcionan, pensé en publicar nuestra solución eventual, que define un método 'listo' que se activará tan pronto como FB se inicialice y esté listo para funcionar. Tiene la ventaja sobre otras soluciones de que es seguro llamar antes o después de que FB esté listo.

Se puede usar así:

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

Aquí está el archivo fuente (tenga en cuenta que está definido dentro de un espacio de nombres '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

He evitado usar setTimeout usando una función global:

EDITAR NOTA: He actualizado los siguientes scripts de ayuda y he creado una clase que es más fácil / simple de usar; compruébalo aquí ::: https://github.com/tjmehta/fbExec.js

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

fbEnsureInit llamará a su devolución de llamada después de FB.init

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

fbEnsureInitAndLoginStatus llamará a su devolución de llamada después de FB.init y después de 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.

      }
    });
  });
}

Uso de ejemplo de fbEnsureInit:

(FB.login debe ejecutarse después de que se haya inicializado FB)

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

Uso de ejemplo de fbEnsureInitAndLogin:

(FB.api debe ejecutarse después de FB.init y el usuario de FB debe iniciar sesión).

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

1
Escribí esta clase de ayuda que funciona igual que la anterior de una manera más fácil y limpia; compruébalo aquí :: github.com/tjmehta/fbExec.js
tjmehta

4

En lugar de usar setTimeout o setInterval, me limitaría a los objetos diferidos (implementación por jQuery aquí ). Todavía es complicado resolver la cola en el momento adecuado, porque init no tiene devoluciones de llamada, pero combinando el resultado con la suscripción al evento (como alguien señaló antes que yo), debería funcionar y estar lo suficientemente cerca.

El pseudo-fragmento tendría el siguiente aspecto:

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

Este es un método más simple, que no requiere eventos ni tiempos de espera. Sin embargo, requiere jQuery.

Utilice jQuery.holdReady() (docs)

Entonces, inmediatamente después de su script jQuery, retrase el evento listo.

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

Luego, en su función de inicio de Facebook, suéltelo:

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

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

Entonces puede usar el evento listo como de costumbre:

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

2

Puedes suscribirte al evento:

es decir)

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

2

Avisos pequeños pero IMPORTANTES:

  1. FB.getLoginStatusdebe invocarse después FB.init, de lo contrario no disparará el evento.

  2. puede usar FB.Event.subscribe('auth.statusChange', callback), pero no se activará cuando el usuario no haya iniciado sesión en Facebook.

Aquí está el ejemplo de trabajo con ambas funciones

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

La API de Facebook busca el FB._apiKey para que pueda ver esto antes de llamar a su propia aplicación de la API con 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();
}; 

No estoy seguro de que esto marque la diferencia, pero en mi caso, porque quería asegurarme de que la interfaz de usuario estuviera lista, he envuelto la inicialización con el documento de jQuery listo (último bit arriba):

  $(document).ready(FBreadyState);

Tenga en cuenta también que NO estoy usando async = true para cargar all.js de Facebook, que en mi caso parece estar ayudando a iniciar sesión en la interfaz de usuario y manejar las funciones de manera más confiable.


1

A veces, fbAsyncInit no funciona. No sé por qué y uso esta solución entonces:

 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);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.