3

我通过将其可见性绑定到一个可观察的命名来在我的 UI 上显示或隐藏一个“加载”指示器waiting,它的定义如下:

// 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>

我正在限制waitingobservable,因为我不希望出现加载消息,除非请求花费很长时间(在这种情况下 > 500 毫秒),以提高应用程序的感知速度。问题是,一旦长时间运行的请求完成,加载指示器直到另外 500 毫秒过去后才会消失。相反,当最后一个未完成的请求完成时,我想立即waiting翻转为 false 。

我第一次尝试使用 进行修复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立即更新?

4

2 回答 2

4

当你扩展一个 observable 时,扩展器通常会用另一个具有所需行为的 observable 包装这个 observable。您可以保留对原始 observable 的引用,这将允许您直接写入它,同时正常公开您的 observable 的节流版本。

例如,

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

waiting在您的特定用例中,不要限制可观察对象,而是限制可outstandingRequests观察对象并使用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());
}

写入您的outstandingRequestsobservable 会立即发生,但您的waitingobservable 将被有效地限制。


或者,我认为更简洁的解决方案是重新实现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());
}
于 2013-10-31T16:46:52.150 回答
2

您真正想要的是延迟来自waiting可观察对象的通知,当它变为true. 这可以通过拦截notifySubscribersobservable 的函数来完成:

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);
    }
};

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

编辑:我只是为您的特定情况想到了另一个可能更好的解决方案。由于waitingobservable 仅依赖于另一个 observable,因此您可以创建手动订阅来更新waitingobservable:

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);

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

于 2013-10-31T20:55:26.883 回答