1

我一直在尝试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() 之后调用this.getSomething()。由于require调用是异步的,我能否以某种方式返回一个promisethis.getSomething()用于确保按顺序调用这两个函数的方法?如果是这样,那怎么办?

阿西姆


更新

使用保罗的想法,这是我处理我的情况的方法:

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是要在其中的承诺函数的首选默认上下文。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!'); }

谢谢保罗=)

4

1 回答 1

3

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

JavaScript 并没有像其他语言那样真正具有wait//概念waitUntilwaitFor您可以在其中等待异步任务完成。事实上,您大多不想这样做,因为它会“挂起”浏览器。

这个 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 对象的函数。

for 的回调require()是内部的,返回的依赖项require()用于解决 deferred。

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

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):

创建使用上述功能的虚拟应用程序模块。

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()将返回一个承诺。我们在链中使用这个 Promise(以证明 Promise 调用可以被链式调用)。首先,将值传递给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
于 2013-09-06T13:41:02.063 回答