Knockout 订阅洞察:检测是否已处置

Knockout subscription insight: detecting if disposed

我的 ko 模型中有一个订阅管理,它通过拦截它们并保存对它们的引用来保存任何订阅。

然后我处理()它们,但有时我需要了解是否已经处理了 sub。成员 Gb,订阅的布尔值 object,是 active-inactive 标志吗?我看到有时当我处理订阅时,它的 Gb 会变成假,有时不会。我必须将此解释为处置失败吗?

一旦我处理订阅,订阅 object 就可以被垃圾收集了吗?

编辑:如标题所述,我只需要了解订阅是否已处理。我以我知道的唯一方式获得订阅,在声明时保存对它的引用:

var mySub = myObservable.subscribe(function(){/*do something*/});

一段时间后,我需要一种方法来确定 mySub 是否已被处置。

function isDisposed(mySubscription) {
    // if is disposed return true else return false
}

我需要基于此执行一些逻辑,如果它还没有被释放(或者我可以简单地再次调用 dispose 方法),则不仅要释放它。是否可以确定订阅处理状态?

关于手动订阅

Knockout 源代码是使用 Google Closure Compiler 编译的,因此只有在源代码中明确导出的属性和方法才会出现在编译的库代码中。

话虽如此,订阅有一个 "private" 属性 isDisposed,但不会导出。因此,为订阅导出的唯一 API 是 dispose.

源代码一瞥-ko.subscription (knockout-3.1.0.debug):

ko.subscription = function (target, callback, disposeCallback) {
    this.target = target;
    this.callback = callback;
    this.disposeCallback = disposeCallback;
    this.isDisposed = false;
    ko.exportProperty(this, 'dispose', this.dispose);
};
ko.subscription.prototype.dispose = function () {
    this.isDisposed = true;
    this.disposeCallback();
};

关于内存泄漏和计算

1) 关于计算的一个有趣事实 - 考虑以下计算

var myObservable1 = ko.observable(true);
var myObservable2 = ko.observable('foo');

var myComputed = ko.computed(function () {
    if (myObservable1()) {
        return myObservable2() + 'bar';
    }
});

在此示例中 myComputed 有 2 个依赖项。但是,如果我们要将 false 分配给 myObservable1 myComputed 将重新评估,并且在重新评估之后它将只有 1 个依赖项并且将处理对 myObservable2 的订阅。

为什么被处置:

解释在于如何评估 computed - 它通过以下方式注册依赖项 - 如果在评估期间读取了任何可观察对象(意味着像 myObservable1() 这样的代码) - computed 收到带有此可观察对象的回调,检查其 id并将其存储在一个新的依赖数组中。评估完成后 - 旧的依赖数组将被处理掉。在我们的示例中,当 myObservable1 设置为 false 时,myObservable2 永远不会被读取——因为我们永远不会进入 if 块。所以它不是一个新的依赖,旧的依赖被处理掉了。

2) 另一个有趣的事实。考虑片段:

(function () {
    var myObservable = ko.observable(0);
    var myModel = {
        myComputed: ko.computed(function () {
            console.log(myObservable());
        })
    };
    myModel = undefined;
    myObservable(42); // Outputs 42
})();

垃圾收集器未收集计算值,因为事实上对它的引用存在于它的依赖项中。

源代码一瞥-ko.computed (knockout-3.1.0.debug):

function addSubscriptionToDependency(subscribable, id) {
    if (!_subscriptionsToDependencies[id]) {
        _subscriptionsToDependencies[id] = subscribable.subscribe(evaluatePossiblyAsync);
        ++_dependenciesCount;
    }
}
...
function evaluatePossiblyAsync() {
    var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
    if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
        clearTimeout(evaluationTimeoutInstance);
        evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
    } else if (dependentObservable._evalRateLimited) {
        dependentObservable._evalRateLimited();
    } else {
        evaluateImmediate();
    }
}

保留对 dependentObservable 的引用,因为保留了对 evaluatePossiblyAsync 的引用(闭包、JS 和所有 Jazz)。

哇,这就是我要说的。希望能想到一些想法。

确定活动 computed 是否已被处置的一种明确方法是查看可观察对象有多少订阅。

这里,看看:

>>> y = ko.observable()
>>> y.getSubscriptionsCount()
0
>>> x = ko.computed(function () { y() })
>>> y.getSubscriptionsCount()
1
>>> x.dispose()
>>> y.getSubscriptionsCount()
0

请注意,对于 pure 计算,当纯计算处于休眠状态时,getSubscriptionsCount 将是 0,即本身没有订阅。即

>>> x = ko.pureComputed(function () { y() })
>>> y.getSubscriptionsCount()
0
>>> z = x.subscribe(console.log.bind(console))
>>> y.getSubscriptionsCount()
1

换句话说,纯计算可能只是在睡觉时被丢弃。

您可以通过订阅来唤醒纯计算;如果依赖的订阅计数仍然为零,则必须释放它。