木偶+ RequireJs - 延迟加载的问题

时间:2013-09-06 11:58:00

标签: requirejs marionette

我一直在尝试使用Mariotte练习Require.js模块的依赖项解析。最近,我试图学习如何懒惰地提取文件,即。仅在需要时加载相关的.js文件,而不是在应用程序启动时加载。然而,事实证明这是一场噩梦。以下代码片段是我现在正在尝试做的事情。

define(['app'], function(App) {
    App.module('Header', function(Header, App, Backbone, Marionette, $, _) {

        this.doSomething = function() { alert('Did something!'); }
        this.getSomething = function() { return 'Something'; }

    }
});

假设doSomething需要加载subdep才能完成其工作。我可以确保如下:我可以在ChromeDevtools中验证subdep仅在调用this.doSomething()时加载。

this.doSomething = function() {
    require(['subdep'], _.bind(function() {
        alert(this.SubDep.showSomething());
    }, this));
}

从现在开始,我有几个问题/问题,我期待着解决。

  1. 我需要使用_.bind()来保留this的值。 require(...)也会直观地污染代码。通过定制Marionette.Module.prototype可以解决这个问题吗?这样的事情是理想的:

    this.doSomething = function() { alert(this.SubDep.showSomething()); }
    myRequireHash: {
        'this.doSomething' : ['subdep'],
        'this.getSomething' : ['subdep', 'underscore']
    }
    
  2. 假设this.getSomething需要向调用者返回一个值。显然,以下内容不起作用,因为require语句启动异步加载并立即返回。我该如何解决这个问题?我需要在实际需要时加载依赖 ,并且还能够返回值

    this.getSomething = function() {
        require(['subapp'], _.bind(function() {
            return this.SubApp.getSomething();
        }, this));
    }
    
  3. 作为第2点的扩展,假设呼叫者需要拨打this.doSomething() AFTER 呼叫this.getSomething()。由于require调用是异步的,我可以以某种方式从promise返回this.getSomething(),可以用来确保这两个函数按顺序调用 吗?如果是这样,那怎么样?

  4. 阿西


    更新

    使用Paul的想法,以下是我解决问题的方法:

    function RequirePromise (pListDeps, oContext, fnFunc)
    {
        return $.Deferred(function(oDef)
        {
            require(pListDeps, function()
            {
                var pArgs = [];
                if (fnFunc)
                {
                    if (oContext) fnFunc = _.bind(fnFunc, oContext);
                    pArgs.push(fnFunc.apply(fnFunc, arguments));
                }
                oDef.resolveWith(oContext, _.union(pArgs, arguments));
            });
        }).promise();
    }
    

    pListDeps是要加载的依赖项列表。 oContext是promise函数的首选默认上下文。fnFunc是一个在给定函数中运行的可选函数(不链接到then函数)。函数的返回值可用作then / done中的第一个参数。加载的依赖项可用作第二个参数。我可以使用以下任何一种方法:

    RequirePromise(['app'], this).done(function(App) { console.log(arguments); }
    
    RequirePromise(['app'], this, function(App) { console.log(App); return true; })
        .done(function(oRet, App) { console.log(arguments); }
    
    RequirePromise(['app'], this)
        .then(function() { return RequirePromise(['subapp']); })
        .done(function() { console.log('Both app and subapp loaded!'); }
    

    谢谢Paul =)

1 个答案:

答案 0 :(得分:3)

如果不为所有异步方法提供回调或延迟/承诺,就无法真正做到你想要的。

与其他语言一样,JavaScript实际上没有wait / waitUntil / waitFor概念,您可以等待异步任务完成。事实上,你大多不想这样做,因为它会“挂起”浏览器。

This jsfiddle就是我的意思。

下面的JS假设了解jQuery Deferreds

HTML:

<script>
require = {
    paths: {
        "jquery": "http://code.jquery.com/jquery-2.0.3",
        "mymodule": "https://rawgithub.com/gitgrimbo/5689953/raw/9b44d7e5f504b2245331be3ed3fcbb7bf8635da6/gistfile1"
    }
};
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.8/require.min.js"></script>
<button>click me</button>

JS(1):

dfdRequire是一个调用require()但返回jQuery promise对象的函数。

require()的回调是内部回调,require()返回的依赖关系用于解决延迟。

基本上,这会从基于回调的系统转换为基于承诺的系统。

function dfdRequire(deps) {
    return $.Deferred(function(dfd) {
        require(deps, function() {
            dfd.resolve.apply(dfd, arguments);
        });
    }).promise();
}

这实际上可以简化为:

function dfdRequire(deps) {
    return $.Deferred(function(dfd) {
        require(deps, dfd.resolve.bind(dfd));
    }).promise();
}

JS(2):

创建使用上述功能的虚拟app模块。

getSomething()方法将使用dfdRequire()加载"mymodule"模块,然后使用then()方法实际使用该模块。

传递给then()的函数通过大写它来使用mymodule值,并返回这个新值。这意味着当方法返回时,它实际上将返回大写的值。

define("app", ["jquery"], function($) {
    return {
        doSomething: function(value) {
            console.log("doSomething with " + value);
        },
        getSomething: function() {
            // Load the dependency as a promise, and return that promise to the caller,
            // so the caller can also await its resolution.
            return dfdRequire(["mymodule"]).then(function(mymodule) {
                console.log("Loaded mymodule. Value=" + mymodule);
                // return the module value as-is,
                // or optionally perform some transformation.
                return mymodule.toUpperCase();
            });
        }
    };
});

JS(3):

拉入应用程序并使用其方法。

app.getSomething()将返回一个承诺。我们在链中使用此承诺(以证明可以链接承诺调用)。首先,将值传递给console.log(),它将打印该值。然后我们致电app.doSomething()

require(["jquery", "app"], function($, app) {
    console.log($.fn.jquery);
    $("button").first().click(function(evt) {
        console.log(evt);
        app.getSomething()
            .done(console.log.bind(console))
            .done(app.doSomething.bind(app));
    });
});

我们使用Function.bind()作为也可以写成的简写。

        app.getSomething()
            .done(function(value) {
                console.log(value);
            })
            .done(function(value) {
                app.doSomething(value);
            });

结果:

2.0.3
Object { originalEvent=Event click, type="click", timeStamp=88448339, more...}
Loaded mymodule. Value=my-library
MY-LIBRARY
doSomething with MY-LIBRARY