如何使用多个$ .ajax调用实现异步计算的observable?

时间:2014-09-10 20:18:53

标签: javascript jquery arrays ajax knockout.js

我试图将异步计算的observable实现为show here

我可以成功完成一次ajax调用。我目前面临的挑战是如何在循环构建数组的循环中执行各种ajax调用,然后使用jQuery promise将数组返回到我的计算可观察数组。

基本上,HTML表单的工作方式如下:

  1. 这是一个学生课程表。
  2. 对于每一行,用户键入人员编号,在另一列上,他们将键入由逗号分隔的课程ID列表。例如100,200,300。
  3. 计算的observable的目的是存储数组 包含在步骤2中输入的课程的课程详细信息。
  4. 详细信息是通过为每个课程触发ajax调用并在数组中存储HTTP响应来获得的。
  5. 我不希望用户等待结果,因此实现异步计算的observable的原因。
  6. 我的问题:我在将最终数组的值返回给observable时遇到了问题。它总是未定义的。 ajax调用工作正常,但也许我还没有正确处理这些承诺。

    这是我班级的代码:

    function asyncComputed(evaluator, owner) {
                var result = ko.observable(), currentDeferred;
                result.inProgress = ko.observable(false); // Track whether we're waiting for a result
    
                ko.computed(function () {
                    // Abort any in-flight evaluation to ensure we only notify with the latest value
                    if (currentDeferred) { currentDeferred.reject(); }
    
                    var evaluatorResult = evaluator.call(owner);
                    // Cope with both asynchronous and synchronous values
                    if (evaluatorResult && (typeof evaluatorResult.done == "function")) { // Async
                        result.inProgress(true);
                        currentDeferred = $.Deferred().done(function (data) {
                            result.inProgress(false);
                            result(data);
                        });
                        evaluatorResult.done(currentDeferred.resolve);
                    } else // Sync
                        result(evaluatorResult);
                });
    
                return result;
            }
    
    
            function personDetails(id, personNumber, courseIds) {
                var self = this;
                self.id = ko.observable(id);
                self.personNumber = ko.observable(personNumber);
                self.courseIds = ko.observable(courseIds);
    
                // Computed property to extract PIC details for additional PICs.
                // This is computed observable which returns response asynchronously
                self.courseDetails = asyncComputed(function () {
                    var courseIdsArray = self.courseIds().split(",");
                    var arr = [];
                    var arr_promises = [];
    
                    function getCourseDetails(courseId) {
                        var dfrd = $.Deferred();
                        var content = {};
    
                        content.searchString = courseId;
    
                        var url = 'MyURL';
    
                        return $.ajax(url, {
                            type: 'POST',
                            dataType: 'json',
                            data: requestData, // content of requestData is irrelevant. The ajax call works fine.
                            processdata: true,
                            cache: false,
                            async: true,
                            contentType: "application/json"
                        }).done(function (data) {
                            arr.push(new PicDetails(data.GenericIdentifierSearchResult[0]));
                        }).fail(function () {
                            alert("Could not retrieve PIC details");
                        }).then(function () {
                            dfrd.resolve();
                        });
    
                    }
    
                    if (courseIdsArray.length > 0) {
    
                        $.each(courseIdsArray, function (index, courseId) {
                            if (courseId.length > 0) {
                                arr_promises.push(getCourseDetails(courseId));
                            }
                        });
                    };
    
                    $.when.apply($, arr_promises).done(function () {
                        return arr;
                    })
    
                }, this);
            } 
    

2 个答案:

答案 0 :(得分:1)

我认为你真的不需要单独的api /代码。

您可以为网站上发生变化的每个输入/值创建可观察对象,并根据这些输入/值创建计算可观察对象。

例如粗略的伪代码

self.id           = ko.observable(id);
self.personNumber = ko.observable(personNumber);
self.courseIds    = ko.observable(courseIds);
self.courseDetailsArray = ko.observableArray([]);
self.courseDetails = ko.computed(function() {
    //computed the course details based on other observables
    //whenever user types in more course ids, start loading them
    $.get( yoururl, {self.courseIds and self.id}).success(data) {
        when finished async loading, parse the data and push the new course details into final array
        self.courseDetailsArray.push( your loaded and parsed data );
        //since courseDetailsArray is observableArray, you can have further computed observables     using and re-formatting it.
    }
});

答案 1 :(得分:0)

我的方法与您的方法略有不同,但如果您愿意,可以构建类似asyncComputed的内容:

  1. 制作一个包含结果的简单可观察
  2. 制作一份承诺字典,您基本上会与课程ID数组保持同步
  3. 当课程ID数组更改时,在promises字典中添加/删除
  4. 将所有承诺包裹在when中(就像你正在做的那样)并在完成后设置结果
  5. 基本理念:

    var results = ko.observable([]);
    var loadingPromises = {};
    var watcher = ko.computed(function () {
    
        var ids = ko.unwrap(listOfIds);
    
        if (ids && ids.length) {
            ids.forEach(function (id) {
                if (!loadingPromises.hasOwnProperty(id)) {
                    loadingPromises[id] = $.get(url, {...id...});
                }
            });
    
            var stillApplicablePromises = {};
            var promises = []; // we could delete from loadingPromises but v8 optimizes delete poorly
            Object.getOwnPropertyNames(loadingPromises).forEach(function (id) {
                if (ids.indexOf(id) >= 0) {
                    stillApplicablePromises[id] = loadingPromises[id];
                    promises.push(loadingPromises[id]);
                }
            });
            loadingPromises = stillApplicablePromises;
    
            $.when.apply(this, promises).then(function () {
                // process arguments here however you like, they're the responses to your promises
                results(arguments);
            });
    
        } else {
            loadingPromises = {};
            results([]);
        }
    }, this);
    

    这是您可以在现实生活中看到的文件(可能会更改):https://github.com/wikimedia/analytics-dashiki/blob/master/src/components/wikimetrics-visualizer/wikimetrics-visualizer.js

    基本的小提琴:http://jsfiddle.net/xtsekb20/1/