The MutationObserver's callback should be executed before updated DOM's rendering which happens in the end of a macrotask, otherwise if you update DOM in the callback there will be a flicker/screen tearing since the callback will be called AFTER the rendering, in another macrotask.
On the other hand you can change DOM sync with many mutations. If MutationObserver would be sync that would lead to excessive amount of events plus possibly additional mutations in the callback leading to infinite recursion.
Sometimes you need to fight the recursion in the callback even with the async nature of MutationObserver.
Here we mutate the style attribute 2 times. Imagine if the amount of props would be bigger and each prop triggers MutationObserver.
Consider when you in the callback the DOM is "settled" in the sync phase of the macrotask. So you have more control of what happens later.
In the code below it's clear that the observe callback is queued sync along with the other microtasks.
If you move the mouse cursor mostly horizontally then the first mutation happens and the observe callback is scheduled immediately. Further DOM mutations just add mutations to the mutation list passed to the callback.
So back to our callback, check the black marks on X and Y axis, we mutate their style on the observe callback. If the callback is executed in another macrotask the marks would be out of sync with the pointer circle because the marks and the pointer would be rendered separately in their synced state and there could be additional mutations to the pointer between the macrotasks.
document.addEventListener('mousemove', e => {
queueMicrotask(()=>console.log('microtask before mutations'));
$pointer.style.left = e.x + 'px';
console.log('left mutated');
queueMicrotask(()=>console.log('microtask between mutations'));
$pointer.style.top = e.y + 'px'
console.log('top mutated');
queueMicrotask(()=>console.log('microtask after mutations'));
});
new MutationObserver(()=>{
console.log(`moved ${$pointer.style.left} ${$pointer.style.top}`);
const {left, top} = $pointer.getBoundingClientRect();
$xaxis.style.left = left + 'px';
$yaxis.style.top = top + 'px';
}).observe($pointer, {attributes:true});
.pointer{
width:16px;
height:16px;
border: 1px solid gray;
border-radius:50%;
position: fixed;
margin: -8px 0 0 -8px;
}
.axis-mark{
width:16px;
height:16px;
background:black;
left: 0;
top:0;
position: fixed;
}
<div class="pointer" id="$pointer"></div>
<div class="axis-mark" id="$xaxis"></div>
<div class="axis-mark" id="$yaxis"></div>