Javascript 在返回之前等待异步完成
这段代码返回一个config对象,并最终用knockout可观测值填充它Javascript 在返回之前等待异步完成,javascript,asp.net-mvc,knockout.js,durandal,amd,Javascript,Asp.net Mvc,Knockout.js,Durandal,Amd,这段代码返回一个config对象,并最终用knockout可观测值填充它 define("config", [], function () { var config = {}; $.ajax({ url: "api/Config", success: function (result) { for (pname in result) config[pname] = ko.observable(result[pname]); }, }
define("config", [], function () {
var config = {};
$.ajax({
url: "api/Config",
success: function (result) {
for (pname in result)
config[pname] = ko.observable(result[pname]);
},
});
return config;
});
它的工作原理是用设置填充config对象,但直到我的一个视图应用了绑定之后才完成,这会在运行时导致问题
在返回配置对象之前,如何设置它以等待ajax查询的结果?我的第一个想法是使用promise对象,但我不知道如何应用它
我不是在寻找解决办法,我已经有了:
define("config", [], function () {
var config = {
IsReady: ko.observable()
};
$.ajax({
url: "api/Config",
success: function (result) {
jquery.extend(config, result);
config.IsReady(true);
},
});
return config;
});
有了这个设置,我像这样绑定
<img data-bind="attr: { src: config.IsReady() ? config.Logo : '' }" />
这会运行,但问题是如何让应用程序的其余部分等待,直到发生这种情况。因此,我将main.js的其余部分放在success函数中,如下所示
$.ajax({
url: "api/Config",
success: function (result) {
define("config", [], result);
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'],
function (system, app, viewLocator) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Jumbo File Transfer';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our app with a transition.
app.setRoot('viewmodels/shell', 'entrance');
});
});
}
});
var foo;
$.ajax({
url:"whatever",
async: false
}).done(function(result){
foo = result;
});
//code that needs result
这实际上是按照正确的顺序执行的——我已经完成了。但应用程序无法启动。如果我不得不猜测原因,我会说define希望这个
成为全局上下文
Nathan下面的回答并没有阻止Durandal正确启动,但config define的行为并不完全正确。我需要将config定义为一个充满设置属性的对象,而不是一个带有工厂方法的对象。但我们就快到了。它只需要像这样:
define('configFactory', ['plugins/http'], function (http) {
"use strict";
var getConfig = function () {
return http.get("api/Config").then(function (data) { return data; });
};
return { getConfig: getConfig };
});
define(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'configFactory'],
function (system, app, viewLocator, configFactory) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Jumbo File Transfer';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
configFactory.getConfig().then(function (config) {
define('config', [], config);
app.start();
}).then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our app with a transition.
app.setRoot('viewmodels/shell', 'entrance');
});
});
事后看来 您可以通过将async选项设置为false来生成ajax块,如下所示
$.ajax({
url: "api/Config",
success: function (result) {
define("config", [], result);
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'],
function (system, app, viewLocator) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Jumbo File Transfer';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our app with a transition.
app.setRoot('viewmodels/shell', 'entrance');
});
});
}
});
var foo;
$.ajax({
url:"whatever",
async: false
}).done(function(result){
foo = result;
});
//code that needs result
然而,引入瓶颈很少是个好主意
下面是一个基本问题:下面的操作将失败,因为在第一次渲染过程之后,配置才具有属性foo
<span data-bind="text:config.foo"><span>
在foreach绑定的模板中不需要像这样显式,因为模板是为每个实例呈现的。您只需预先声明可观察的array这是承诺的一个用例。我不确定javascript是否支持跨浏览器,但它似乎与其他异步语言类似。 如果使用jquery,它会变得更简单:
$.ajax({
url: "http://fiddle.jshell.net/favicon.png",
beforeSend: function( xhr ) {
xhr.overrideMimeType( "text/plain; charset=x-user-defined" );
}
}).done(function( data ) {
if(console && console.log ) {
console.log( "Sample of data:", data.slice( 0, 100 ) );
}
});
您不能像现在这样返回
config
。你就是不能。ajax调用是异步的,因此它将立即返回,您将返回一个空的config
对象。使用异步ajax处理此问题的唯一方法是为异步代码设计。您可以使用promises,也可以从成功处理程序调用函数,并将现在完成的config
对象传递给它。无论哪种方式,您都将在回调函数中获得完成的config
对象
define("config", [], function () {
var config = {};
$.ajax({
url: "api/Config",
success: function (result) {
for (pname in result) {
config[pname] = ko.observable(result[pname]);
}
// call your function now and pass it the completed config object
callMyFunction(config);
}
});
});
您可能认为这是一种解决方法,但这是异步操作的编码方式。我建议使用in-durandal来处理此问题
define(['plugins/http', 'services/logger'], function (http, logger) {
"use strict";
var getConfig = function () {
return http.get("api/Config").then(function (data) {
return data;
});
};
return {
getConfig: getConfig
};
});
这应该是一个行动的问题
define(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'config'],
function (system, app, viewLocator, config) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Jumbo File Transfer';
app.configurePlugins({
router: true,
dialog: true,
widget: true,
http: true
});
config.getConfig().then(app.start()).then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our app with a transition.
app.setRoot('viewmodels/shell', 'entrance');
});
});
编辑我认为您只需要在ajax调用的
done
函数中添加您所拥有的内容,就可以得到您所需要的
define('configFactory', ['plugins/http'], function (http) {
"use strict";
var getConfig = function () {
return http.get("api/Config").then(function (data) {
var config = {};
for (pname in data){
config[pname] = ko.observable(data[pname]);
}
return config;
});
};
return { getConfig: getConfig };
});
有一个更简单的答案。
如果您使您的
config
可见,则可以稍后更新:
define("config", [], function () {
var config = ko.observable({}); // 1. Make config observable
$.ajax({
url: "api/Config",
success: function (result) {
//for (pname in result) {
// config[pname] = ko.observable(result[pname]);
//}
config(result); // 2. Replace the existing config object
},
});
return config;
});
因此,您的html将更新为:
<img data-bind="attr: { src: config().Logo }" />
这对于在aync操作完成时安排其他行为很好,但不会阻止调用上下文。我可以将main.js的其余部分放在done处理程序中,该处理程序确实解决了特定的问题,但它没有教我如何同步异步进程。我现在可以从您的编辑中看到。好吧,我怀疑你能摆脱它,因为即使你将config.Logo设置为一个包含该逻辑的包装器对象,它在执行.done/success之前是没有定义的。。这让我想到了一个问题:为什么这个观点是超前的?如果可能的话,我会改变这一点。在收到ajax响应之前,您不应该构建或呈现视图吗?在Backbone.js中,在ajax获取集合之前,不会构造视图。我想,knockout.js也是如此。这几乎可以奏效。请参阅我的问题编辑。对您的输入非常感兴趣。哪一位不起作用?你是说一小时前的编辑吗?不是,是最新的。对不起,我已经偏离了方向。你知道吗,我差点就这么做了。可能应该有,就像你说的更简单。但有时它是关于旅程的。这不起作用,因为config()在绑定时没有Logo属性。