当外部包不可用且仍然 运行 domReady 时,有没有办法优雅地失败?
Is there a way to gracefully fail when an external package is not available and still run domReady?
我正在 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 文档进行了一些挖掘,以弄清楚为什么我的 ready 没有触发,运行 通过这个推理 (domReady):
Now comes along dojo/domReady!. The loader loads all the dependencies
for dojo/domReady! and then demands the plugin resource be resolved.
But dojo/domReady! may not be able to resolve the demanded plugin
resource (an empty module ID which is intended to signal the DOM is
ready) because the DOM may not be ready. The loader notices this an
sees that the module was not capable of being loaded synchronously,
gives up and continues.
This is an intentional limitation in the loader, since handling it
would have required more complicated code. It will cease to be an
issue for Dojo 2.0, when the synchronous loader is desupported.
据我所知,只要无法加载依赖项,Dojo 就会停止尝试。很好,但是有没有 任何 方式来优雅地处理外部库不可用的情况?
我想也许有一些方法可以通过 XHR 调用来完成,但这会导致跨源问题。我唯一的其他解决方案是将它们添加为单独的脚本标签,这是加载其他一些库的方式;但是由于这个模块现在正在重构,我想尝试让它尽可能地与 AMD 兼容。
你可以创建一个promise,在require成功的时候resolve;并在 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;
});
is a really close solution to resolving the issue at hand. Effectively, since you can't be sure that the require
will ever finish, you can't try to resolve or reject a promise in it. I ended up using a dojo/Deferred
in conjunction with dojo.io.script
以便让 Twitter API 异步加载,然后在 Twitter API 加载时调用的回调中解决承诺。
还要记住我使用的是 Dojo 1.7.2。从 Dojo 1.8 开始,dojo.io.script
被弃用,取而代之的是 dojo/request/script
。我没有仔细研究两者之间的区别。
此代码可能可以优化得更好一些,例如,我认为您不需要使用 declare
或者如果需要,您可以创建一个基础 API class,如果您尝试在一个页面上多次使用或加载 Twitter API,我不确定它是否工作得很好。然而,它满足了我的需要,我已经调整了这个解决方案,以类似的方式加载 Youtube、Google 和 Facebook APIs。
Twitter.js
基本上,代码归结为使用 dojo.io.script
让 Dojo 在页面上放置一个带有 Twitter src 的 <script>
标签,让我们获得 Twitter 使用的全局,twttr
并尝试确保它不会在不需要时这样做。 Twitter 有一个 "ready" 回调,它通过 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
在我的小部件中,我有开关可以让我禁用社交 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;
});
我正在 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 文档进行了一些挖掘,以弄清楚为什么我的 ready 没有触发,运行 通过这个推理 (domReady):
Now comes along dojo/domReady!. The loader loads all the dependencies for dojo/domReady! and then demands the plugin resource be resolved. But dojo/domReady! may not be able to resolve the demanded plugin resource (an empty module ID which is intended to signal the DOM is ready) because the DOM may not be ready. The loader notices this an sees that the module was not capable of being loaded synchronously, gives up and continues.
This is an intentional limitation in the loader, since handling it would have required more complicated code. It will cease to be an issue for Dojo 2.0, when the synchronous loader is desupported.
据我所知,只要无法加载依赖项,Dojo 就会停止尝试。很好,但是有没有 任何 方式来优雅地处理外部库不可用的情况?
我想也许有一些方法可以通过 XHR 调用来完成,但这会导致跨源问题。我唯一的其他解决方案是将它们添加为单独的脚本标签,这是加载其他一些库的方式;但是由于这个模块现在正在重构,我想尝试让它尽可能地与 AMD 兼容。
你可以创建一个promise,在require成功的时候resolve;并在 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
will ever finish, you can't try to resolve or reject a promise in it. I ended up using a dojo/Deferred
in conjunction with dojo.io.script
以便让 Twitter API 异步加载,然后在 Twitter API 加载时调用的回调中解决承诺。
还要记住我使用的是 Dojo 1.7.2。从 Dojo 1.8 开始,dojo.io.script
被弃用,取而代之的是 dojo/request/script
。我没有仔细研究两者之间的区别。
此代码可能可以优化得更好一些,例如,我认为您不需要使用 declare
或者如果需要,您可以创建一个基础 API class,如果您尝试在一个页面上多次使用或加载 Twitter API,我不确定它是否工作得很好。然而,它满足了我的需要,我已经调整了这个解决方案,以类似的方式加载 Youtube、Google 和 Facebook APIs。
Twitter.js
基本上,代码归结为使用 dojo.io.script
让 Dojo 在页面上放置一个带有 Twitter src 的 <script>
标签,让我们获得 Twitter 使用的全局,twttr
并尝试确保它不会在不需要时这样做。 Twitter 有一个 "ready" 回调,它通过 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
在我的小部件中,我有开关可以让我禁用社交 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;
});