当外部程序包不可用且仍然运行domReady时,有没有办法优雅地失败?

时间:2015-01-21 19:01:48

标签: javascript dojo

我正在Dojo 1.7.2中创建一个共享栏小部件,并使用require()有条件地将外部社交媒体API加载为包,如下所示:

require({
    packages:[
        {name: "twitter-api", location: "https://platform.twitter.com", main: "widgets"}
    ]
},["twitter-api"], lang.hitch(this, function(){
    // Do some social stuff with twitter API
}));

每当我能够访问Twitter API时,这都很有效,但现在我正在通过专用网络对其进行测试,但事实并非如此。问题不在于窗口小部件不会工作。相反,我有一些额外的代码在ready()内运行,同样适用于domReady(),在尝试加载这些软件包后无法加载。

我对Dojo文档进行了一些挖掘,以弄清楚为什么我的准备工作没有解雇并且遇到了这个推理(domReady):

  

现在来了dojo / domReady!。加载器加载所有依赖项   for dojo / domReady!然后要求解决插件资源。   但是dojo / domReady!可能无法解决所需的插件   resource(用于表示DOM的空模块ID)   准备好了因为DOM可能还没准备好。装载机注意到这一点   看到模块无法同步加载,   放弃并继续。

     

这是加载程序中的故意限制,因为它处理它   本来需要更复杂的代码。它将不再是一个   当不支持同步加载器时,Dojo 2.0的问题。

从我收集的内容来看,只要无法加载依赖项,Dojo就会停止尝试。这没关系,但有没有任何方式来优雅地处理外部库不可用的情况?

我认为可能有某种方法可以通过XHR调用来实现,但这会导致跨源问题。我唯一的另一个解决方案就是将它们作为单独的脚本标记添加,这是其他几个库的加载方式;但由于此模块现在正在重构,我想尝试尽可能使其符合AMD标准。

2 个答案:

答案 0 :(得分:1)

您可以创建承诺并在需求成功时解决它;并且在promise的错误处理程序中优雅地失败:

require(["dojo/Deferred"], function(Deferred){
    var d = new Deferred();
    require(['twitter-api'], function(twitterApi){
        d.resolve(twitterApi);
    });
    d.then(function(api){
        // use api
    }, function(err){
        // fail gracefully
    });
    return d.promise;
});

答案 1 :(得分:1)

Stafamus' answer是解决手头问题的非常紧密的解决方案。实际上,由于您无法确定require是否会完成,因此您无法尝试解决或拒绝其中的承诺。我最终使用dojo/Deferreddojo.io.script结合使用,以便异步加载Twitter API,然后解决Twitter API在加载时调用的回调内的承诺。

另请记住,我使用的是 Dojo 1.7.2 。从 Dojo 1.8 开始,不推荐使用dojo.io.script,而是dojo/request/script。我没有研究过两者之间的区别。

这段代码可能会稍微优化一下,例如我不认为您需要使用declare,或者如果您这样做,您可以创建一个基本API类,而且我可以如果您尝试在页面上多次使用或加载Twitter API,我不确定它是否工作得非常好。但是,它符合我的需求,我已经调整了这个解决方案,也以类似的方式加载Youtube,Google和Facebook API。

Twitter.js

基本上,代码归结为使用dojo.io.script让Dojo在页面上放置一个<script>标签和Twitter src,这样就可以获得Twitter使用的全局twttr并尝试如果不需要,请确保它不会这样做。 Twitter有一个&#34;准备好&#34;通过t.ready()定义的回调,我们通过引用twttr全局来解决我们的API承诺。实际上它已经是一个Dojo包装方式Twitter tells you to do来加载JavaScript API。

define([
    "dojo/_base/declare",
    "dojo/_base/Deferred",
    "dojo/io/script"
], function (
        declare,
        Deferred,
        ioScript
        ) {

    /**
     * @class api/Twitter
     * @classdesc Wrapper for the Twitter API which isn't inherently AMD
     */
    var Twitter = declare("api.Twitter", [], {
        /**
         * The deferred that we use to get a handle to API
         * @member {dojo/Deferred} api/Twitter#apiDeferred
         */
        apiDeferred: null,
        /**
         * The constructor which loads the API and starts the player creation
         * @method api/Twitter#constructor
         * @param {HTMLElement} container The id of the element to hold the player
         */
        constructor: function (container) {
            // Create a deferred that will resolve when we have a player
            this.apiDeferred = this.loadAPI();
        },
        /**
         * Function that loads the API from Twitter asynchronously
         * @method api/Twitter#loadAPI
         * @return {dojo/Deferred} The deferred that will resolve when the API is loaded
         */
        loadAPI: function () {

            // Create an API deferred to resolve when the API is loaded
            var apiDeferred = new Deferred();

            // Make sure that we haven't already loaded the Twitter API
            if (typeof twttr === "undefined") {

                /*
                 * This is where we wrap up the normally "synchronous" loading
                 * of whatever API we're using.  Twitter is slightly asynchronous
                 * in that it loads itself through creating a script tag and setting 
                 * the src which we can do with dojo.io.script already.  However,
                 * twitter expects there to exist a "twttr" variable whenever it loads
                 * much like dojo expects a dojoConfig.  In the twttr, there is an array
                 * of callbacks _e that will be called once the API is loaded.  This is
                 * where we create the boilerplate for Twitter. 
                 */

                // Create the twttr object from an immediately executed functor
                window.twttr = (function () {
                    // Make sure we don't already have a twttr object
                    var t = window.twttr || {};
                    // Create a ready callback array
                    t._e = [];
                    // Create a wrapper to allow pushing to the ready array
                    t.ready = function (f) {
                        t._e.push(f);
                    };
                    // Add our on callback that will let us know twitter loaded
                    t.ready(function (e) {
                        // Deliver the API through the deferred
                        apiDeferred.resolve(e.widgets);
                    });
                    // Return the twitter object
                    return t;
                }());

                // Ask dojo to load a new script tag with the Twitter API as the src
                ioScript.get({
                    url: "https://platform.twitter.com/widgets.js",
                    error: function (e) {
                        apiDeferred.reject("Twitter API seems incorrectly loaded.");
                    }
                });
            }
            // If we already have the Twitter API, we can resolve right now
            else {
                if (typeof twttr.widgets !== "undefined") {
                    apiDeferred.resolve(twttr.widgets);
                }
                else {
                    apiDeferred.reject("Twitter API seems incorrectly loaded.");
                }
            }

            // Return the API Deferred
            return apiDeferred;
        },
        /**
         * Function to get a handle to the deferred that should resolve to the twitter API
         * @member api/Twitter#getAPIDeferred
         * @returns {dojo/Deferred} The Twitter API
         */
        getAPIDeferred: function () {
            return this.apiDeferred;
        }
    });

    // Return our class
    return Twitter;
});

ShareBar.js

在我的小部件中,我有开关让我禁用Social API,如果我想使用它,我只需要上面包含的API。然后我创建一个新的Twitter,询问API Deferred,如果它结算,我创建Twitter按钮。此文件被严重截断,并且不包括所有窗口小部件代码,仅包括API代码的实际使用。

define([
    "dojo/_base/declare",
    "Twitter"
], function (
        declare,
        Twitter
        ) {
    /**
     * @class ShareBar
     * @classdesc Widget used to display social media functionality on a page.
     */
    var ShareBar = declare({
        /**
         * By requiring the Twitter API, we have the twttr JS 
         * object which we can use to do Twitter stuff like 
         * parsing tweet buttons
         * @method ShareBar#_loadTwitter
         */
        _loadTwitter: function () {
            var
                    // Create a twitter module
                    twitter = new Twitter(),
                    // Get a closure safe handle to our twitter button
                    twitterButton = this.TwitterButton,
                    // Create a callback for when the twitter API loads
                    twitterSuccess = function (twitterAPI) {
                        // Run the twitter parser on our twitter button
                        twitterAPI.load(twitterButton);
                        // Allow the twitter button to be seen
                        domClass.remove(twitterButton, "hiddenSocialButton");
                    },
                    // Create an errback for if the twitter API fails
                    twitterFail = function (err) {
                        // Note the error
                        console.log(err);
                    };
            // Wait for the API to resolve and use the callbacks
            twitter.getAPIDeferred().then(twitterSuccess, twitterFail);
        }
    });

    // Return widget
    return ShareBar;
});