Skip to content

Commit 01d6a75

Browse files
committed
try parallelizing awaited $state
1 parent a819a67 commit 01d6a75

File tree

3 files changed

+91
-16
lines changed

3 files changed

+91
-16
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,45 +176,83 @@ export function VariableDeclaration(node, context) {
176176
const value = /** @type {Expression} */ (args[0]) ?? b.void0; // TODO do we need the void 0? can we just omit it altogether?
177177

178178
if (rune === '$state' || rune === '$state.raw') {
179+
const state_declarators = [];
180+
const current_chunk = context.state.current_parallelized_chunk;
181+
const parallelize =
182+
declarator.id.type === 'Identifier' &&
183+
context.state.analysis.instance?.scope === context.state.scope &&
184+
value.type === 'AwaitExpression' &&
185+
can_be_parallelized(value.argument, context.state.scope, context.state.analysis, [
186+
...(current_chunk?.bindings ?? []),
187+
...bindings
188+
]);
179189
/**
180190
* @param {Identifier} id
191+
* @param {Expression} visited
181192
* @param {Expression} value
182193
*/
183-
const create_state_declarator = (id, value) => {
194+
const create_state_declarator = (id, visited, value) => {
184195
const binding = /** @type {Binding} */ (context.state.scope.get(id.name));
185196
const is_state = is_state_source(binding, context.state.analysis);
186-
const is_proxy = should_proxy(value, context.state.scope);
197+
const is_proxy = should_proxy(visited, context.state.scope);
198+
const compose = [];
199+
if (parallelize) {
200+
if (rune === '$state' && is_proxy) {
201+
compose.push(b.id('$.proxy'));
187202

188-
if (rune === '$state' && is_proxy) {
189-
value = b.call('$.proxy', value);
203+
if (dev && !is_state) {
204+
compose.push(
205+
b.arrow([b.id('proxy')], b.call('$.tag_proxy', b.id('proxy'), b.literal(id.name)))
206+
);
207+
}
208+
}
190209

191-
if (dev && !is_state) {
192-
value = b.call('$.tag_proxy', value, b.literal(id.name));
210+
if (is_state) {
211+
compose.push(b.id('$.state'));
212+
if (dev) {
213+
compose.push(
214+
b.arrow([b.id('source')], b.call('$.tag', b.id('source'), b.literal(id.name)))
215+
);
216+
}
217+
}
218+
} else {
219+
if (rune === '$state' && is_proxy) {
220+
value = b.call('$.proxy', value);
221+
222+
if (dev && !is_state) {
223+
value = b.call('$.tag_proxy', value, b.literal(id.name));
224+
}
193225
}
194-
}
195226

196-
if (is_state) {
197-
value = b.call('$.state', value);
227+
if (is_state) {
228+
value = b.call('$.state', value);
198229

199-
if (dev) {
200-
value = b.call('$.tag', value, b.literal(id.name));
230+
if (dev) {
231+
value = b.call('$.tag', value, b.literal(id.name));
232+
}
201233
}
202234
}
203235

204-
return value;
236+
return parallelize && value.type === 'AwaitExpression'
237+
? b.call(
238+
'$.async_compose',
239+
/** @type {Expression} */ (context.visit(value.argument)),
240+
...compose
241+
)
242+
: visited;
205243
};
206244

207245
if (declarator.id.type === 'Identifier') {
208246
const expression = /** @type {Expression} */ (context.visit(value));
209247

210-
declarations.push(
211-
b.declarator(declarator.id, create_state_declarator(declarator.id, expression))
248+
state_declarators.push(
249+
b.declarator(declarator.id, create_state_declarator(declarator.id, expression, value))
212250
);
213251
} else {
214252
const tmp = b.id(context.state.scope.generate('tmp'));
215253
const { inserts, paths } = extract_paths(declarator.id, tmp);
216254

217-
declarations.push(
255+
state_declarators.push(
218256
b.declarator(tmp, /** @type {Expression} */ (context.visit(value))),
219257
...inserts.map(({ id, value }) => {
220258
id.name = context.state.scope.generate('$$array');
@@ -236,12 +274,36 @@ export function VariableDeclaration(node, context) {
236274
return b.declarator(
237275
path.node,
238276
binding?.kind === 'state' || binding?.kind === 'raw_state'
239-
? create_state_declarator(binding.node, value)
277+
? create_state_declarator(binding.node, value, path.expression)
240278
: value
241279
);
242280
})
243281
);
244282
}
283+
if (!parallelize) {
284+
declarations.push(...state_declarators);
285+
} else {
286+
const declarators = state_declarators.map(({ id, init }) => ({
287+
id,
288+
init: /** @type {Expression} */ (init)
289+
}));
290+
if (current_chunk && (current_chunk.kind === node.kind || current_chunk.kind === null)) {
291+
current_chunk.declarators.push(...declarators);
292+
current_chunk.bindings.push(...bindings);
293+
current_chunk.position = position;
294+
current_chunk.kind = node.kind;
295+
} else {
296+
/** @type {ParallelizedChunk} */
297+
const chunk = {
298+
kind: node.kind,
299+
declarators,
300+
position,
301+
bindings
302+
};
303+
context.state.current_parallelized_chunk = chunk;
304+
context.state.parallelized_chunks.push(chunk);
305+
}
306+
}
245307

246308
continue;
247309
}

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export {
101101
export {
102102
all,
103103
async_body,
104+
async_compose,
104105
for_await_track_reactivity_loss,
105106
save,
106107
track_reactivity_loss

packages/svelte/src/internal/client/reactivity/async.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,15 @@ export function all(...promises) {
202202
)
203203
);
204204
}
205+
206+
/**
207+
* @param {Promise<any>} promise
208+
* @param {Array<(arg: any) => any>} fns
209+
*/
210+
export async function async_compose(promise, ...fns) {
211+
let res = await promise;
212+
for (const fn of fns) {
213+
res = fn(res);
214+
}
215+
return res;
216+
}

0 commit comments

Comments
 (0)