如何强制受限制的observable立即更新?

时间:2013-10-31 16:38:59

标签: knockout.js computed-observable

我显示或隐藏"正在加载"通过将其可见性绑定到名为waiting的可观察对象,我的UI上的指示符,其定义如下:

// Viewmodel
var outstandingRequests = ko.observable(0);

// true if any requests are outstanding
var waiting = ko.computed(function() {
    return outstandingRequests() > 0;
}.extend({ throttle: 500 });

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
}

<!-- View -->
<div data-bind="visible: waiting">Please wait, loading...</div>

我限制了waiting观察点,因为我不希望加载消息出现,除非请求花费很长时间(在这种情况下大于500毫秒),以增加感知申请的速度。问题是,一旦长时间运行的请求完成,加载指示器就不会消失,直到另外500ms过去。相反,当最后一个未完成的请求完成时,我希望waiting立即翻转为

我首次尝试使用valueHasMutated()进行修复,但更新仍然延迟。

function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
    // If that was the last request, we want the loading widget to disappear NOW.
    outstandingRequests.valueHasMutated(); // Nope, 'waiting' still 500ms to update :(
}

如何绕过油门延长并强制waiting立即更新?

2 个答案:

答案 0 :(得分:4)

当你扩展一个observable时,扩展器通常会将observable与另一个具有所需行为的observable包装起来。您可以保留对原始observable的引用,这将允许您直接写入它,同时正常暴露您的可观察的受限制版本。

如,

var myObservable = ko.observable('foo');
var myThrottledObservable = myObservable.extend({ throttle: 500 });
myThrottledObservable('bar'); // delayed
myObservable('baz'); // immediate

在您的特定用例中,不是限制waiting observable,而是限制outstandingRequests observable并使用waiting中的限制值。

var outstandingRequests = ko.observable(0);

// throttled requests for the waiting observable
var throttledOutstandingRequests = outstandingRequests.extend({ throttle: 500 });

// true if any requests are outstanding
var waiting = ko.computed(function() {
    return throttledOutstandingRequests() > 0;
};

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
}

立即写入您的outstandingRequests观察点,但您的waiting观察点将被有效限制。


或者,我认为更清洁的解决方案是重新实现throttled扩展程序以添加立即更新的功能。

ko.extenders['throttleEx'] = function(target, timeout) {
    // Throttling means two things:

    // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
    //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
    target['throttleEvaluation'] = timeout;

    // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
    //     so the target cannot change value synchronously or faster than a certain rate
    var writeTimeoutInstance = null;
    var throttled = ko.dependentObservable({
        'read': target,
        'write': function(value) {
            clearTimeout(writeTimeoutInstance);
            writeTimeoutInstance = setTimeout(function() {
                target(value);
            }, timeout);
        }
    });

    // add function to set the value directly
    throttled['immediate'] = function(value) {
        target(value);
    };

    return throttled;
};

然后使用它:

var waiting = ko.computed(function() {
    return outstandingRequests() > 0;
}.extend({ throttleEx: 500 });

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests.immediate(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests.immediate(--outstandingRequests());
}

答案 1 :(得分:2)

您真正想要的是在waiting true notifySubscribers观察时延迟通知。这可以通过拦截observable的var originalNotifySubscribers = this.isWaiting.notifySubscribers, timeoutInstance; this.isWaiting.notifySubscribers = function(value, event) { clearTimeout(timeoutInstance); if ((event === 'change' || event === undefined) && value) { timeoutInstance = setTimeout(function() { originalNotifySubscribers.call(this, value, event); }.bind(this), 500); } else { originalNotifySubscribers.call(this, value, event); } }; 函数来完成:

waiting

jsFiddle:http://jsfiddle.net/mbest/Pk6mH/

编辑: 我只想到了另一种可能更好的解决方案,针对您的具体案例。由于waiting observable仅依赖于另一个observable,因此您可以创建一个更新var timeoutInstance; this.isLoading.subscribe(function(value) { clearTimeout(timeoutInstance); if (value) { timeoutInstance = setTimeout(function() { this.isWaiting(true); }.bind(this), 500); } else { this.isWaiting(false); } }, this); 可观察对象的手动订阅:

{{1}}

jsFiddle:http://jsfiddle.net/mbest/wCJHT/