Javascript 当外部包不可用且仍在运行domReady时,是否有一种方法可以正常失败?
我正在Dojo 1.7.2中创建一个共享栏小部件,并使用Javascript 当外部包不可用且仍在运行domReady时,是否有一种方法可以正常失败?,javascript,dojo,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 soci
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文档上做了一些挖掘,以找出为什么我的ready没有启动,并遇到了以下推理():
现在是dojo/domReady!。加载程序加载所有依赖项
对于dojo/domReady!然后要求解析插件资源。
但是dojo/domReady!可能无法解析所需的插件
resource(一个空的模块ID,用于向DOM发送信号)
准备就绪)因为DOM可能尚未准备就绪。加载程序注意到这一点
发现模块无法同步加载,
放弃并继续
这是加载程序中的故意限制,因为要处理它
可能需要更复杂的代码。这将不再是一场战争
当同步加载程序不受支持时,Dojo 2.0的问题
从我收集的信息来看,每当无法加载依赖项时,Dojo就会停止尝试。这很好,但是有没有任何方法可以优雅地处理外部库不可用的情况
我想也许有一些方法可以通过XHR调用实现,但这会导致跨源问题。我唯一的其他解决方案是将它们作为单独的脚本标记添加,这就是加载其他一些库的方式;但是由于这个模块现在正在进行重构,我想尝试使它尽可能与AMD兼容。您可以创建一个承诺,并在需求成功时解决它;并在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;
});
这是一个非常接近于解决当前问题的解决方案。实际上,由于您无法确定require
是否会完成,因此无法尝试解决或拒绝其中的承诺。最后,为了异步加载twitterapi,我结合使用了,然后从回调中解析了twitterapi在加载时调用的承诺
还请记住,我正在使用Dojo 1.7.2。从Dojo 1.8开始,Dojo.io.script
被弃用,取而代之的是。我没有研究两者之间的区别
这段代码可能会优化得更好一些,例如,我认为您不需要使用声明
,或者如果您这样做了,您可以创建一个基本API类,并且如果您尝试在一个页面上多次使用或加载推特API,我不确定它是否工作得非常好。然而,它已经满足了我的需要,我也调整了这个解决方案,以类似的方式加载Youtube、Google和Facebook API
Twitter.js
基本上,代码归结为使用dojo.io.script
让dojo在页面上放置一个带有Twitter src的
标记,从而获得Twitter使用的全局变量,twttr
,并尝试确保它不需要这样做。Twitter有一个“ready”回调,它是通过t.ready()
定义的,我们在中通过引用twttr
全局函数来解析我们的API承诺。实际上,这是一种Dojo包装的方式来加载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
在我的小部件中,我有一些开关可以让我禁用社交API,如果我想使用,我只需要上面包装好的API。然后我创建一个新的Twitter
,请求延迟API,如果它解决了,我创建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;
});
您的错误回调实际上永远不会用这段代码到达,因为没有任何东西告诉延迟者拒绝。适当地拒绝充其量只是一种挑战或捏造,因为您的选择基本上是设置一个超时,或者尝试使用dojo loader的events API,它可以在模块加载失败时告诉您,但您也可能最终接收到您不感兴趣的模块的事件。谢谢Ken@zero298,这是一个困难的问题,Ken在这里的建议还有很长的路要走。感谢@stafamus这就是我如何有效地解决问题的方法,除了我依靠常规的API就绪回调来解决承诺之外。当我有时间时,我将尝试发布一个完整的实现示例。