使用Promises使用JavaScript加载jQuery

之前的一个问题揭示了如何使用本机JavaScript加载jQuery。 我已经成功地使用了那里答案的回调代码,在这里复制:

// Anonymous "self-invoking" function (function() { // Load the script var script = document.createElement("SCRIPT"); script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'; script.type = 'text/javascript'; document.getElementsByTagName("head")[0].appendChild(script); // Poll for jQuery to come into existance var checkReady = function(callback) { if (window.jQuery) { callback(jQuery); } else { window.setTimeout(function() { checkReady(callback); }, 100); } }; // Start polling... checkReady(function($) { // Use $ here... }); })(); 

如何使用本机JavaScript Promises完成同样的事情?

我问的原因是因为我突然需要将早期的回调链接起来,这是一个混乱的问题。 我希望Promises是一种更好的方式,我对使用加载器框架没有兴趣。

这是我到目前为止所得到的,但承诺总是被拒绝:

 // This code doesn't work quite right. // Poll for jQuery to come into existance using a Promise var jQueryReady = new Promise( function(resolve, reject) { // Load jQuery var script = document.createElement('SCRIPT'); script.type = 'text/javascript'; script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'; document.getElementsByTagName('head')[0].appendChild(script); if (window.jQuery) { resolve("YAY"); } else { reject("UGH"); } }); jQueryReady.then( function(success) { console.log(success); }, function(error) { console.error("Really helpful error:", error); }); 

(我很抱歉提前完全无知。)

这是一个版本,它创建一个简单的loadScript()函数,它返回一个promise,然后提供一个包装器来检测是否已经加载了jQuery:

 function loadScript(url) { return new Promise(function(resolve reject) { var script = document.createElement("script"); script.onload = resolve; script.onerror = reject; script.src = url; document.getElementsByTagName("head")[0].appendChild(script); }); } function loadjQuery() { if (window.jQuery) { // already loaded and ready to go return Promise.resolve(); } else { return loadScript('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'); } } // Usage: loadjQuery().then(function() { // code here that uses jQuery }, function() { // error loading jQuery }); 

关于您的代码的注释:

  1. 在您的第一个代码块中,设置一个计时器并假设在该计时器触发时将加载脚本就像玩轮盘赌一样。 它可能在大多数时候都有效,但它不是一种纯粹可靠的做事方法。 此外,为了安全起见,您必须将计时器设置为比通常所需的更长的时间段。 相反,您应该根据脚本的onload回调触发。 然后,您将确切知道脚本何时准备就绪,具有100%的可靠性。

  2. 在你的第二个代码块中,你的promise版本成功处理了jQuery已经加载的情况,但是当jQuery必须自定义加载时rejects() 。 从我的示例中可以看出,您需要resolve()何时新加载的脚本标记已完成加载,以使您的承诺按预期工作。

以这种方式插入页面的脚本是异步执行的。 (请参阅MDN上的“动态导入脚本” 。)因此, window.jQuery将始终undefined

尝试将onload处理程序附加到script元素,以便只在脚本执行后才执行Promise的解析。

例如(在设置script.src之前):

 script.onload = function () { if (window.jQuery) { resolve("YAY"); } else { reject("UGH"); } }; 

像往常一样,此方法与所有浏览器都不兼容。 查看这篇文章,了解如何容纳它们。

问题是这是一种加载javascript的异步非阻塞方式。 您必须等待浏览器下载脚本。

这是一个可能的解决方案:

 var jQueryReady = new Promise( function(resolve, reject) { // Load jQuery var script = document.createElement('SCRIPT'); script.type = 'text/javascript'; script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'; document.getElementsByTagName('head')[0].appendChild(script); // You should also have a timeout in case your script never loads var timeoutHandler = setTimeout(10000, function() { if (checkIntervalHandler) clearInterval(checkIntervalHandler); reject("UGH"); }); var checkIntervalHandler = setInterval(500, function() { if (window.jQuery) { if(timeoutHandler) clearTimeout(timeoutHandler); resolve("YAY"); } }); });