等待异步完成,然后返回 [英] Wait for async completion before returning

查看:90
本文介绍了等待异步完成,然后返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码返回一个配置对象,并最终使用可剔除的可观察对象填充该对象.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆