[whatwg/dom] [discussion] Async nature of MutationObserver can cause room for error? (#399)

I ran into an interesting case.

A library that I'm working on creates components that contain HTMLElements. The user can run some code like this:

```js
parent.removeChild(child)
parent.addChild(child)
```

Behind the scenes, the synchronous `removeChild` and `addChild` methods each trigger a `MutationRecord` to be recorded for the element referenced by `parent`. This means that at the next microtask `parent` element's MutationObserver's callback will be called with two MutationRecords: one for `removedNodes` and the other for `addedNodes`.

Ironically, the MutationObserver callback calls `parent.removeChild()` on children that were removed from parent, but the `removeChild()` call would normally be a no-op had `parent.AddChild()` not also been called in the same turn. `parent.addChild()` sets some state that causes `removeChild()` not to be a no-op, and vice versa.

Basically, if MutationObservation were synchronous, then the MutationObserver would fire two separate callbacks in the above example: one for `removedNodes` directly after the `removeChild()` call, and one for `addedNodes` directly after the `addChild()` call. If this were the case, the MutationObserver callback would fire those second `removeChild` and `addChild` calls as no-ops because the reciprocal calls would not have happened in the same turn.

When two MutationRecords are fed to a single async MutationObserver callback, this is causing `removeChild` and `addChild` to fire again, but not as no-ops, and this causes two more MutationRecords to be pushed for the next microtask. The same thing happens on the next microtask which is causing an infinite loop.

Considering that MutationObserver is not synchronous, I can temporarily work around the problem by changing the user code to the following, which causes the `removedNodes` and `addedNodes` reactions to be interleaved with the `removeChild` and `addChild` calls respectively:

```js
~async function() {
 parent.removeChild(child)
 await sleep(1000) // give time for MutationObserver callback to fire with only removeNodes
 parent.addChild(child)
 // A following MutationObserver callback fires with only addedNodes
}()
```

Do you get what I mean? I suppose I am wondering if a synchronous API would be better (f.e. a library author might not overlook async behavior like I did). One way in which there could be a synchronous API for this is with [`childConnectedCallback` and `childDisconnectedCallback`](https://github.com/w3c/webcomponents/issues/550) for the Custom Element API.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/399

Received on Monday, 16 January 2017 05:01:39 UTC