使用Promises

时间:2016-01-06 16:42:36

标签: javascript jquery promise

A previous question revealed how to load in jQuery using native JavaScript.我已经成功地使用了那里答案的回调代码,在这里复制:

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

如何使用native JavaScript Promises?

完成同样的事情

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

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

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

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

4 个答案:

答案 0 :(得分:8)

这是一个版本,它创建一个简单的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()

答案 1 :(得分:1)

当您有多个具有依赖项的脚本时,我发现这样的 Promise 序列效果最好:

let p1 = loadScript('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js')
let p2 = loadScript('scripts/moment.min.js')
            
Promise.allSettled([p1, p2])
.then(function() {
    return loadScript('scripts/myScript.js')
}).then(function() {
    console.log('All Scripts Loaded!')
    initMyScript()
})
.catch(error => {
    console.error(error.message)
})


function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script')
    script.src = src
    script.onload = resolve
    script.onerror = reject
    document.head.append(script)
  })
}

答案 2 :(得分:0)

以这种方式插入页面的脚本是异步执行的。 (请参阅MDN上的"Dynamically importing scripts"。)因此,window.jQuery始终为undefined

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

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

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

与往常一样,此方法与所有浏览器都不兼容。检查this post out是否有适应它们的方法。

答案 3 :(得分:0)

问题是这是一种加载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");
      }
    });
});