Jak wykryć zakończenie FB.init na Facebooku

115

Stary JS SDK miał funkcję o nazwie FB.ensureInit. Wydaje się, że nowy SDK nie ma takiej funkcji ... jak mogę się upewnić, że nie wykonuję wywołań interfejsu API, dopóki nie zostanie w pełni zainicjowany?

Umieszczam to na górze każdej strony:

<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
źródło
Zobacz ten link, aby znaleźć moje rozwiązanie tego problemu. facebook.stackoverflow.com/questions/12399428/ ...
Remi Grumeau

Odpowiedzi:

138

Aktualizacja z 4 stycznia 2012 r

Wygląda na to, że nie możesz po prostu wywoływać metod zależnych od FB (na przykład FB.getAuthResponse()) zaraz po FB.init()jak poprzednio, ponieważ teraz FB.init()wydaje się być asynchroniczny. Pakowanie kodu w FB.getLoginStatus()odpowiedź wydaje się wystarczać do wykrycia, kiedy API jest w pełni gotowe:

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

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

};  

lub jeśli korzystasz fbEnsureInit()z implementacji od dołu:

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

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

};  

Oryginalny post:

Jeśli chcesz po prostu uruchomić jakiś skrypt podczas inicjalizacji FB, możesz umieścić w nim funkcję wywołania zwrotnego 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
  };

Jeśli chcesz dokładnego zastąpienia FB.ensureInit, musisz napisać coś samodzielnie, ponieważ nie ma oficjalnego zamiennika (duży błąd imo). Oto czego używam:

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

Stosowanie:

fbEnsureInit(function() {
    console.log("this will be run once FB is initialized");
});
serg
źródło
2
Dziękuję Serg… jesteś legendą!
Pablo
3
Nie mogę uruchomić żądania API Facebooka natychmiast po wywołaniu FB.init - być może dlatego, że używam metody „fql.query”? Tak czy inaczej, muszę ustawić długi limit czasu na fladze fbApiInit.
Ian Hunter
Dzięki za aktualizację - miałem ten sam problem, a zaktualizowane rozwiązanie działało idealnie!
Terri Ann
@beanland, fql.query wywołania API wymagają, aby użytkownik zalogował się. Jesteś pewien, że używasz zapytanie po otrzymaniu responsez powrotem z FB.getLoginStatus(function(response){}? - zwróć uwagę na różnicę między „zaktualizowaną odpowiedzią” a „pierwotną odpowiedzią” powyżej.
tjmehta
@serg dziękuję za przykład. Musiałem ustawić opóźnienie w mojej funkcji fbAsyncInit, aby była wywoływana po załadowaniuwindow.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

W rzeczywistości Facebook już zapewnił mechanizm subskrybowania wydarzeń uwierzytelniania.

W twoim przypadku używasz " status: true ", co oznacza, że ​​obiekt FB zażąda od Facebooka statusu logowania użytkownika.

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

Wywołując „FB.getLoginStatus ()” ponownie uruchamiasz to samo żądanie .

Zamiast tego możesz użyć FB.Event.subscribe, aby zasubskrybować zdarzenie auth.statusChange lub auth.authResponseChange PRZED wywołaniem 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
});

Najprawdopodobniej używając " status: false " możesz uruchomić dowolny kod zaraz po FB.init, ponieważ nie będzie wywołań asynchronicznych.

shpoont
źródło
UWAGA: W przypadku tej metody jedynym zastrzeżeniem jest to, że jeśli nie jesteś zalogowany, zdarzenie nie zostanie wyzwolone. Po prostu ustaw domyślny stan interfejsu użytkownika tak, jakby nie był uwierzytelniony i działa idealnie. Następnie możesz sprawdzić, czy w programie obsługi zdarzeń nie ma połączenia lub nie jest ono autoryzowane.
David C
Ponadto, dla aplikacji front-end, aby utrzymać status logowania użytkownika między odświeża, zestaw cookiedo truew obiekcie startowych param.
MK Safi
12

Oto rozwiązanie na wypadek, gdybyś używał i Facebook asynchroniczne leniwe ładowanie:

// 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
źródło
3
Kocham to! Jedynym problemem jest to, że Facebook nalega, aby dołączyć kod inicjujący FB bezpośrednio po otwierającym tagu <body>, a najlepsza praktyka mówi, że powinieneś załadować jQuery przed zamykającym tagiem <body>. No! Kurczak i jajko. Kusi mnie, żeby powiedzieć „pieprz się, Facebook” i załadować FB po jQuery.
Ethan Brown
@EthanBrown Zgadzam się - Twoja strona prawdopodobnie potrzebuje więcej jQuery niż FB, więc to ma sens. Zrezygnowałem z ładowania jQuery na końcu strony lata temu ;-)
Simon_Weaver
dodaję version: 'v2.5'init json innym mądrym to nie zadziała .. przynajmniej nie dla mnie. dzięki za pomoc
Vikas Bansal
10

Innym sposobem sprawdzenia, czy FB został zainicjowany, jest użycie następującego kodu:

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

W związku z tym w zdarzeniu gotowym strony możesz sprawdzić ns.FBInitialized i odroczyć zdarzenie do późniejszej fazy za pomocą setTimeOut.

user2253630
źródło
To jest prawdziwa rzecz! Dzięki!
Thiago Macedo
To jest proste. Podoba mi się
Faris Rayhan
5

Chociaż niektóre z powyższych rozwiązań działają, pomyślałem, że opublikuję nasze ostateczne rozwiązanie - które definiuje „gotową” metodę, która zostanie uruchomiona, gdy tylko FB zostanie zainicjowany i gotowy do pracy. Ma tę przewagę nad innymi rozwiązaniami, że można bezpiecznie zadzwonić przed lub po przygotowaniu FB.

Można go używać w następujący sposób:

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

Oto plik źródłowy (zauważ, że jest zdefiniowany w przestrzeni nazw „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
źródło
4

Uniknąłem używania setTimeout, używając funkcji globalnej:

EDYTUJ UWAGA: Zaktualizowałem następujące skrypty pomocnicze i utworzyłem klasę, która jest łatwiejsza / prostsza w użyciu; sprawdź to tutaj ::: https://github.com/tjmehta/fbExec.js

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

fbEnsureInit wywoła swoje wywołanie zwrotne po FB.init

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

fbEnsureInitAndLoginStatus wywoła swoje wywołanie zwrotne po FB.init i po 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.

      }
    });
  });
}

Przykładowe użycie fbEnsureInit:

(FB.login należy uruchomić po zainicjowaniu FB)

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

Przykład zastosowania fbEnsureInitAndLogin:

(FB.api musi być uruchomiony po FB.init, a użytkownik FB musi być zalogowany.)

fbEnsureInitAndLoginStatus(function(){
    FB.api(
       //..enter code here
    );
});
tjmehta
źródło
1
Napisałem tę klasę pomocniczą, która działa tak samo jak powyżej w łatwiejszy i bardziej przejrzysty sposób; sprawdź tutaj: github.com/tjmehta/fbExec.js
tjmehta
4

Zamiast używać dowolnego setTimeout lub setInterval, trzymałbym się obiektów odroczonych (implementacja przez jQuery tutaj ). Wciąż trudno jest rozwiązać kolejkę w odpowiednim momencie, ponieważ init nie ma callbacków, ale połączenie wyniku z subskrypcją zdarzenia (jak ktoś wcześniej wskazał), powinno załatwić sprawę i być wystarczająco blisko.

Pseudo-snippet wyglądałby następująco:

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
źródło
4

Oto prostsza metoda, która nie wymaga ani zdarzeń, ani limitów czasu. Wymaga jednak jQuery.

Użyj jQuery.holdReady() (dokumenty)

Zatem natychmiast po skrypcie jQuery opóźnij zdarzenie 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>

Następnie, w funkcji inicjalizacji Facebooka, zwolnij ją:

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

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

Następnie możesz normalnie skorzystać z ready event:

$(document).ready( myApp.init );
voidstate
źródło
2

Możesz zapisać się na wydarzenie:

to znaczy)

FB.Event.subscribe('auth.login', function(response) {
  FB.api('/me', function(response) {
    alert(response.name);
  });
});
Jonathan Tonge
źródło
2

Małe, ale WAŻNE uwagi:

  1. FB.getLoginStatusmusi zostać wywołane później FB.init, w przeciwnym razie zdarzenie nie zostanie uruchomione.

  2. możesz użyć FB.Event.subscribe('auth.statusChange', callback), ale nie uruchomi się, gdy użytkownik nie jest zalogowany na Facebooku.

Oto działający przykład z obiema funkcjami

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
źródło
1

Interfejs API Facebooka wyszukuje FB._apiKey, więc możesz go obserwować przed wywołaniem własnej aplikacji API za pomocą czegoś takiego:

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

Nie jestem pewien, czy to robi różnicę, ale w moim przypadku - ponieważ chciałem mieć pewność, że interfejs użytkownika jest gotowy - zapakowałem inicjalizację z gotowym dokumentem jQuery (ostatni kawałek powyżej):

  $(document).ready(FBreadyState);

Zwróć też uwagę, że NIE używam async = true do ładowania pliku all.js Facebooka, co w moim przypadku wydaje się pomagać w bardziej niezawodnym logowaniu się do interfejsu użytkownika i sterowaniu funkcjami.

Jimmont
źródło
1

Czasami fbAsyncInit nie działa. Nie wiem dlaczego, więc używam tego obejścia:

 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);
patena
źródło