我一直在尝试使用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));
}
从现在开始,我有几个问题/问题,我期待着解决。
我需要使用_.bind()
来保留this
的值。 require(...)
也会直观地污染代码。通过定制Marionette.Module.prototype
可以解决这个问题吗?这样的事情是理想的:
this.doSomething = function() { alert(this.SubDep.showSomething()); }
myRequireHash: {
'this.doSomething' : ['subdep'],
'this.getSomething' : ['subdep', 'underscore']
}
假设this.getSomething
需要向调用者返回一个值。显然,以下内容不起作用,因为require
语句启动异步加载并立即返回。我该如何解决这个问题?我需要在实际需要时加载依赖 ,并且还能够返回值。
this.getSomething = function() {
require(['subapp'], _.bind(function() {
return this.SubApp.getSomething();
}, this));
}
作为第2点的扩展,假设呼叫者需要拨打this.doSomething()
AFTER 呼叫this.getSomething()
。由于require
调用是异步的,我可以以某种方式从promise
返回this.getSomething()
,可以用来确保这两个函数按顺序调用 吗?如果是这样,那怎么样?
阿西
更新
使用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 =)
答案 0 :(得分:3)
如果不为所有异步方法提供回调或延迟/承诺,就无法真正做到你想要的。
与其他语言一样,JavaScript实际上没有wait
/ waitUntil
/ waitFor
概念,您可以等待异步任务完成。事实上,你大多不想这样做,因为它会“挂起”浏览器。
This jsfiddle就是我的意思。
下面的JS假设了解jQuery Deferreds。
<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>
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();
}
创建使用上述功能的虚拟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();
});
}
};
});
拉入应用程序并使用其方法。
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