【问题标题】:image loading time and onload function call图像加载时间和 onload 函数调用
【发布时间】:2015-06-19 07:06:32
【问题描述】:

下面的列表是 Udacity 面向对象的 JavaScript 课程的最终项目中的青蛙游戏的一部分。它由 Udacity 提供,以下代码的目的是构建一个简单的图像加载缓存。我的问题是在列表之后。吉特:https://github.com/udacity/frontend-nanodegree-arcade-game

(function() {
    var resourceCache = {};
    var loading = [];
    var readyCallbacks = [];
    function load(urlOrArr) {
        if(urlOrArr instanceof Array) {
            urlOrArr.forEach(function(url) {
                _load(url);
            });
        } else {
            _load(urlOrArr);
        }
    }
    function _load(url) {
        if(resourceCache[url]) {
            return resourceCache[url];
        } else {
            var img = new Image();
            img.onload = function() {
                resourceCache[url] = img;
                if(isReady()) {
                    readyCallbacks.forEach(function(func) { func(); });
                }
            };
            resourceCache[url] = false;
            img.src = url;
        }
    }
    function get(url) {
        return resourceCache[url];
    }
    function isReady() {
        var ready = true;
        for(var k in resourceCache) {
            if(resourceCache.hasOwnProperty(k) &&
               !resourceCache[k]) {
                ready = false;
            }
        }
        return ready;
    }
    function onReady(func) {
        readyCallbacks.push(func);
    }
    window.Resources = {
        load: load,
        get: get,
        onReady: onReady,
        isReady: isReady
    };
})(); 

Resources.load([
    'images/stone-block.png',
    'images/water-block.png',
    'images/grass-block.png',
    'images/enemy-bug.png',
    'images/char-boy.png'
]);
Resources.onReady(init);//init function is to start the game

我的问题是关于这条线

img.src = url

我认为在这一行之后,图像将立即加载,并且 img.onload 将立即被触发。我想如果是这样的话,那么:

在img.onload()函数内部,由于此时resourceCache中只有一个属性并且已经设置为'img',所以isReady()调用会返回true,调用readyCallbacks。但是,由于.onReady()函数还没有执行(程序还在Resources.load()函数中),此时readyCallbacks里面什么都没有,所以程序什么也不做,但是稍后,因为img.onload 已被触发,不会再次触发,因此在执行 Resources.onReady() 并将 init() 函数插入到 readyCallbacks 后,不会调用它,因此不会启动游戏。

我正在阅读代码,当我到达这里时,我无法理解。在我一步步运行代码之后,我发现加载图像实际上需要时间,即使它们都是本地的。使用performance.now(),我发现加载一张图片大约需要0.5ms,此时调用_load()的for循环已经完成,第二个和第三个resourceCache的属性都设置为false,所以isReady() 不会评估为真,代码将正常运行。如果只有一张图片要加载,那么在加载图片之前,将调用 .onReady()(在 Resource.load() 之后)并将 init() 插入到 readyCallback 中,代码也可以。

如果我上面的分析没有问题,那么还有一点我还是不明白:我们真的可以依靠这个图像加载时间超过其他代码的执行时间吗?如果图像非常小并且在 resourceCache 的另一个属性设置为“false”或已设置 readyCallback 之前触发 img.onload 怎么办?如果是这样,代码将不起作用。

当我进一步思考时,我无法理解为什么我们必须放置

    if (isReady()) {
        readyCallbacks.forEach(function (func) {
            func();
        });
    }

在 img.onload() 函数中,当我们要加载多个图像时,它会被执行多次。我们可以把它放在 .load() 调用之后吗?毕竟,它的目的是检查是否所有的图像都已加载完毕。

【问题讨论】:

    标签: javascript image performance


    【解决方案1】:

    img.onload 回调被放置在执行队列中,最早直到其余代码运行完成后才会执行。

    isReady 函数被多次调用,因为该函数只有在所有图像都完成下载后才会返回 true。它需要在每个图像之后运行,因为无法知道哪个图像将是最后一个完成加载并因此触发回调的图像。

    【讨论】:

    • "img.onload 回调被放置在执行队列中并且最早直到其余代码运行完成才被执行。"这个我不明白。哪些代码确保在其余代码运行完成之前不会执行 img.onload?只是图像加载时间吗?能否请您指出我分析的哪一部分不正确?
    • 还是被浏览器“放入执行队列”?
    • 所有回调都放入一个事件队列中。然后浏览器按照到达的顺序执行事件队列。然而,浏览器不会在事件队列中启动,直到文件中的所有代码都被处理——这是运行到完成。一旦所有代码都被处理完,浏览器就会在队列中启动。不管加载的速度有多快,回调都会进入队列。它是异步的。
    • 感谢您的信息。您提到“文件”,是指.onload所在的.js文件还是加载javascripts的html文件?
    • 文件或文件,同步读取的任何内容。 Onload 本身就是 callback,因此在从事件队列处理回调之前,不会执行其中的所有代码。
    猜你喜欢
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 2012-12-10
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多