等待异步完成,然后返回 [英] Wait for async completion before returning
问题描述
此代码返回一个配置对象,并最终使用可剔除的可观察对象填充该对象.
This code returns a config object, and eventually populates it with knockout observables.
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对象,但是直到我的其中一个视图应用了绑定后它才能完成,这会在运行时引起问题.
It works in terms of populating the config object with settings, but it doesn't finish until after one of my views has applied bindings, and this causes problems at runtime.
在返回配置对象之前,如何设置它以等待ajax查询的结果?我的第一个想法是使用一个Promise对象,但是我看不到如何应用它.
How would you set this up to wait on the result of the ajax query before returning the config object? My first thought was using a promise object, but I can't see how to apply it.
我不是要寻找一种解决方法,我已经有一个解决方法:
I'm not looking for a workaround, I already have one:
define("config", [], function () {
var config = {
IsReady: ko.observable()
};
$.ajax({
url: "api/Config",
success: function (result) {
jquery.extend(config, result);
config.IsReady(true);
},
});
return config;
});
通过这种设置,我像这样绑定
With this setup I bind like this
<img data-bind="attr: { src: config.IsReady() ? config.Logo : '' }" />
但是我不想用那种垃圾来污染我的观点,所以我想知道如何在从工厂方法返回之前等待异步操作.
But I don't want to pollute my views with that rubbish, so I want to know how to wait on an async operation before returning from a factory method.
试图运用到目前为止收到的建议,我把事情彻底搞砸了.因为result
现在在范围内,并且在调用define时可以使用,所以我直接将其直接传递给define.
Trying to apply advice received so far, I turned things inside out. Because result
is now in scope and ready to use when define is called, I simply pass it directly to define.
$.ajax({
url: "api/Config",
success: function (result) {
define("config", [], result);
}
});
//the following code should not execute until config has been defined.
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');
});
});
这会运行,但是剩下的问题是如何让应用程序的其余部分等到发生这种情况.因此,我将main.js的其余部分放在成功函数中,就像这样.
This runs but leaves the question of how to make the rest of the application wait till this has happened. So I put the rest of main.js inside the success function, like this.
$.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');
});
});
}
});
实际上确实以正确的顺序执行-我逐步完成了它.但是该应用无法启动.如果我不得不猜测为什么,我会说define要this
作为全局上下文.
This actually does execute in the right order - I stepped through it. But the app fails to start. If I had to guess why, I would say define wants this
to be the global context.
Nathan的以下回答并不能阻止Durandal正常启动,但是配置定义的行为不太正确.我需要将config定义为充满设置属性的对象,而不是具有工厂方法的对象.但是我们快到了.它只需要看起来像这样:
Nathan's answer below doesn't stop Durandal from starting properly but the behaviour of the config define isn't quite right. I need config defined as an object full of settings properties, not an object with a factory method. But we're nearly there. It just needs to look like this:
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块.
In hindsight
You can make ajax block by setting the async option to false, like this.
var foo;
$.ajax({
url:"whatever",
async: false
}).done(function(result){
foo = result;
});
//code that needs result
但是,引入瓶颈很少是一个好主意.
However, introducing a bottleneck is seldom a good idea.
这是基本的问题:以下操作将失败,因为config在首次渲染通过之前没有属性foo
.
Here's the fundamental problem: the below will fail because config doesn't have a property foo
until after the first rendering pass.
<span data-bind="text:config.foo"><span>
我们可以在main中同步加载配置,但是更好的答案是将绑定推迟到foo
可用.
We could synchronously load config in main, but a better answer is to defer binding until foo
is available.
<!-- ko if: config.foo -->
<span data-bind="text:config.foo"><span>
...
<!-- /ko -->
对于foreach绑定,您不需要在模板中像这样显式,因为模板是为每个实例呈现的.您只需要预先声明observableArray
.
You don't need to be explicit like this in the template for a foreach binding, since the template is rendered for each instance. You need only pre-declare the observableArray
.
推荐答案
我建议使用 http插件(可在durandal中处理).
I would suggest using the http plugin in durandal to handle this.
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
函数中所拥有的内容即可获得所需的内容.
Edit
I think you just need to add what you had in your done
function of the ajax call to get what you need.
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 };
});
这篇关于等待异步完成,然后返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!