Как определить, что Facebook FB.init завершен


115

В старом JS SDK была функция FB.ensureInit. Новый SDK, похоже, не имеет такой функции ... как я могу гарантировать, что я не буду делать вызовы api, пока он не будет полностью инициирован?

Я добавляю это вверху каждой страницы:

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

Смотрите по этой ссылке мое решение этой проблемы. facebook.stackoverflow.com/questions/12399428/…
Реми Грумо

Ответы:


138

Обновление от 4 января 2012 г.

Похоже, вы не можете просто вызвать FB-зависимые методы (например FB.getAuthResponse()) сразу после того, FB.init()как раньше, поскольку FB.init()теперь это кажется асинхронным. Обертывание вашего кода в FB.getLoginStatus()ответ, похоже, помогает определить, когда API полностью готов:

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

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

};  

или при использовании fbEnsureInit()реализации снизу:

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

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

};  

Исходное сообщение:

Если вы хотите просто запустить какой-то скрипт при инициализации FB, вы можете поместить внутрь некоторую функцию обратного вызова 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
  };

Если вам нужна точная замена FB.ensureInit, вам придется написать что-то самостоятельно, поскольку официальной замены нет (большая ошибка, imo). Вот что я использую:

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

Использование:

fbEnsureInit(function() {
    console.log("this will be run once FB is initialized");
});

2
Спасибо, Серг .. ты легенда!
Пабло

3
Я не могу запустить запрос API Facebook сразу после вызова FB.init - возможно, потому, что я использую метод «fql.query»? В любом случае, я должен установить длительный тайм-аут для флага fbApiInit.
Ян Хантер,

Спасибо за обновление - у меня была такая же проблема, и ваше обновленное решение отлично работало!
Terri Ann

@beanland, fql.query вызовы API требуют, чтобы пользователь вошел в систему . Вы уверены , что вы запускаете запрос после получения responseобратно от FB.getLoginStatus(function(response){}? - обратите внимание на разницу между «обновленным ответом» и «исходным ответом» выше.
tjmehta

@serg спасибо за пример. Мне пришлось установить задержку внутри моей функции, fbAsyncInitчтобы она window.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); };
вызывалась

38

Фактически Facebook уже предоставил механизм для подписки на события аутентификации.

В вашем случае вы используете « status: true », что означает, что объект FB будет запрашивать у Facebook статус входа пользователя.

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.getLoginStatus ()», вы снова выполняете тот же запрос .

Вместо того, чтобы вы могли использовать FB.Event.subscribe подписаться на auth.statusChange или auth.authResponseChange события ПЕРЕД вы звоните 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
});

Скорее всего, при использовании « status: false » вы можете запустить любой код сразу после FB.init, потому что асинхронных вызовов не будет.


ПРИМЕЧАНИЕ. Единственное предостережение при использовании этого метода заключается в том, что если вы не вошли в систему, событие не сработает. Просто сделайте состояние пользовательского интерфейса по умолчанию, как будто оно не аутентифицировано, и оно отлично работает. Затем вы можете проверить наличие connected или not_authorized внутри обработчика событий.
David C

Кроме того, для интерфейсных приложений для поддержания статуса входа пользователя в систему между обновлениями установлено cookieзначение trueв объекте init param.
MK Safi

12

Вот решение, если вы используете и асинхронная отложенная загрузка 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
Я люблю это! Единственная проблема заключается в том, что Facebook настаивает на том, чтобы вы включали код инициализации FB непосредственно после открывающего тега <body>, а в лучших практиках рекомендуется загружать jQuery перед закрывающим тегом <body>. Doh! Курица и яйцо. У меня возникает соблазн сказать: «Да пошли вы, Facebook» и загрузить FB после jQuery.
Итан Браун

@EthanBrown Я согласен - ваша страница, вероятно, нуждается в jQuery больше, чем в FB, так что это имеет смысл. Я бросил попытки загрузить jQuery в конце страницы много лет назад ;-)
Simon_Weaver

добавить version: 'v2.5'в init json, иначе это не сработает .. по крайней мере, не для меня. спасибо за помощь
Викас Бансал

10

Другой способ проверить, инициализирован ли FB, - использовать следующий код:

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

Таким образом, в событии готовности страницы вы можете проверить ns.FBInitialized и отложить событие на более позднюю фазу с помощью setTimeOut.


Это настоящая вещь! Спасибо!
Тьяго Маседо

Это просто. Нравится
Фарис Райхан

5

Хотя некоторые из вышеперечисленных решений работают, я решил опубликовать наше возможное решение, которое определяет «готовый» метод, который срабатывает, как только FB инициализируется и готов к работе. Его преимущество перед другими решениями в том, что его можно безопасно вызывать до или после того, как FB будет готов.

Его можно использовать так:

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

Вот исходный файл (обратите внимание, что он определен в пространстве имен 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

Я избегал использования setTimeout, используя глобальную функцию:

ПРИМЕЧАНИЕ ИЗМЕНИТЬ: Я обновил следующие вспомогательные сценарии и создал класс, который проще / проще в использовании; проверьте здесь ::: https://github.com/tjmehta/fbExec.js

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

fbEnsureInit вызовет обратный вызов после FB.init

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

fbEnsureInitAndLoginStatus вызовет обратный вызов после FB.init и после 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:

(FB.login необходимо запустить после инициализации FB)

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

Пример использования fbEnsureInitAndLogin:

(FB.api необходимо запустить после того, как FB.init и пользователь FB должен войти в систему.)

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

1
Я написал этот вспомогательный класс, который выполняет то же самое, что и выше, более простым и понятным способом; проверьте это здесь :: github.com/tjmehta/fbExec.js
tjmehta

4

Вместо использования каких-либо setTimeout или setInterval я бы придерживался отложенных объектов (реализация jQuery здесь ). По-прежнему сложно разрешить очередь в нужный момент, потому что у init нет обратных вызовов, но объединение результата с подпиской на событие (как кто-то указал передо мной) должен сделать трюк и быть достаточно близким.

Псевдо-фрагмент будет выглядеть следующим образом:

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

Вот более простой метод, который не требует ни событий, ни тайм-аутов. Однако для этого требуется jQuery.

использование jQuery.holdReady() (документы)

Итак, сразу после вашего сценария jQuery отложите событие готовности.

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

Затем в своей функции инициализации Facebook отпустите ее:

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

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

Затем вы можете использовать готовое событие как обычно:

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

2

Вы можете подписаться на событие:

то есть)

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

2

Небольшие, но ВАЖНЫЕ примечания:

  1. FB.getLoginStatusдолжен быть вызван после FB.init, иначе он не вызовет событие.

  2. вы можете использовать FB.Event.subscribe('auth.statusChange', callback), но он не сработает, если пользователь не вошел в facebook.

Вот рабочий пример с обеими функциями

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

API Facebook следит за FB._apiKey, поэтому вы можете следить за этим, прежде чем вызывать собственное приложение API с чем-то вроде:

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

Не уверен, что это имеет значение, но в моем случае - поскольку я хотел убедиться, что пользовательский интерфейс готов - я завернул инициализацию с готовым документом jQuery (последний бит выше):

  $(document).ready(FBreadyState);

Также обратите внимание, что я НЕ использую async = true для загрузки Facebook all.js, что в моем случае, похоже, помогает более надежно войти в пользовательский интерфейс и управлять функциями.


1

Иногда fbAsyncInit не работает. Я не знаю, почему, и тогда использую этот обходной путь:

 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);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.