diff --git a/tko.io/src/content/docs/3to4.md b/tko.io/src/content/docs/3to4.md index 024cd6c10..6bd8752cc 100644 --- a/tko.io/src/content/docs/3to4.md +++ b/tko.io/src/content/docs/3to4.md @@ -54,6 +54,30 @@ TKO is designed for stricter Content Security Policies and avoids the older `eva `ko.applyBindings` returns a `Promise`. If you chain bootstrap work after binding or need to wait for async bindings to finish, account for that in your startup flow. +### `descendantsComplete` no longer waits for async descendants + +In Knockout 3, `descendantsComplete` fired only after every descendant — including content rendered asynchronously by a nested `if`, `template`, `foreach`, or component — had finished binding. Knockout tracked this with an ancestor-linked async-completion context and, on control-flow bindings, a `completeOn: 'render'` option. + +TKO drops that ancestor-tracking machinery in favor of a simpler per-node model. As a result, `descendantsComplete` is decided **once**, synchronously, when the node itself binds, and waits only for async bindings **on that same node** — not for subtrees that appear later. If a descendant `if`/`template` renders its content after the fact (for example when its condition flips to `true`), the `descendantsComplete` callback is not re-triggered. + +**Why:** the async-completion context was cross-cutting, hard to reason about, and a common source of disposal and ordering bugs. The per-node lifecycle is smaller and predictable, at the cost of this one edge case. + +**Migration:** use `childrenComplete` instead, which is notified every time a node's descendants are (re-)bound and therefore still fires when async content renders: + +```html + +
+
+
+``` + +```html + +
+
+
+``` + ## What is still familiar - `ko.observable`, `ko.observableArray`, and `ko.computed`