ko with asynch computed

by nickkell

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<input type="number" data-bind="value: currentPage" />
<div data-bind="text: ko.toJSON($root)"></div>

JavaScript

ko.extenders.async = function (target, option) {
    var asyncDependentObservable = function (evaluator, owner) {
        var result = ko.observable(),
            currentDeferred;

        result.loading = ko.observable(false); // Track whether we're waiting for a result
        ko.dependentObservable(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.loading(true);
                currentDeferred = $.Deferred()
                .done(function (data) {
                    result(data);
                })
                .always(function () {
                    result.loading(false);
                });
                evaluatorResult.always(currentDeferred.resolve);
            } else { // Sync
                result(evaluatorResult);
            }
        });

        return result;
    };

    var o = asyncDependentObservable(option.deferred, option.owner || target());

    return o;
};

function VM() {
    var self = this;

    self.currentPage = ko.observable(1);

    self.results = ko.observable().extend({
        async: {
            deferred: function() {
                return $.post('/echo/json/', {
                    json: JSON.stringify(self.currentPage())
                }, 'json').pipe(function(json) {
                    return json;
                });
            }
        }
    });
}

var vm = new VM();
ko.applyBindings(vm);