avoid reawakening committed batches

pull/16971/head
Rich Harris 1 month ago
parent bf9065448a
commit 14ba41e7fd

@ -1,5 +1,5 @@
/** @import { Effect, TemplateNode, Value } from '#client' */
import { DESTROYED } from '#client/constants';
import { DESTROYED, STALE_REACTION } from '#client/constants';
import { DEV } from 'esm-env';
import {
component_context,

@ -76,6 +76,8 @@ let is_flushing = false;
export let is_flushing_sync = false;
export class Batch {
committed = false;
/**
* The current values of any sources that are updated in this batch
* They keys of this map are identical to `this.#previous`
@ -399,6 +401,7 @@ export class Batch {
batch_values = previous_batch_values;
}
this.committed = true;
batches.delete(this);
this.#deferred?.resolve();

@ -127,7 +127,17 @@ export function async_derived(fn, location) {
// If this code is changed at some point, make sure to still access the then property
// of fn() to read any signals it might access, so that we track them as dependencies.
// We call `unset_context` to undo any `save` calls that happen inside `fn()`
Promise.resolve(fn()).then(d.resolve, d.reject).then(unset_context);
Promise.resolve(fn())
.then(d.resolve, d.reject)
.then(() => {
if (batch === current_batch && batch.committed) {
// if the batch was rejected as stale, we need to cleanup
// after any `$.save(...)` calls inside `fn()`
batch.deactivate();
}
unset_context();
});
} catch (error) {
d.reject(error);
unset_context();

@ -21,5 +21,9 @@ export default test({
input.dispatchEvent(new Event('input', { bubbles: true }));
await macrotask(6);
assert.htmlEqual(target.innerHTML, '<input> 3 | 12');
input.value = '';
input.dispatchEvent(new Event('input', { bubbles: true }));
await macrotask();
assert.htmlEqual(target.innerHTML, '<input> 4 | ');
}
});

@ -1,28 +1,32 @@
<script>
let count = $state(0);
let value = $state('');
let prev;
let resolver;
function asd(v) {
const r = Promise.withResolvers();
let r = Promise.withResolvers();
if (prev || v === '') {
Promise.resolve().then(async () => {
count++;
r.resolve(v);
await new Promise(r => setTimeout(r, 0));
// TODO with a microtask like below it still throws a mutation error
// await Promise.resolve();
prev?.resolve();
});
} else {
prev = Promise.withResolvers();
prev.promise.then(() => {
count++;
r.resolve(v)
});
function update_and_resolve(){
count++;
r.resolve(v);
}
// make sure the second promise resolve before the first one
if(resolver){
new Promise(r => {
setTimeout(r);
}).then(update_and_resolve).then(()=>{
setTimeout(()=>{
resolver();
resolver = null;
});
});
}else if(v){
resolver = update_and_resolve;
}else{
Promise.resolve().then(update_and_resolve);
}
return r.promise;
}

Loading…
Cancel
Save