The PianoRhythm initialization system prevents race conditions through a dependency-based state machine. This guide provides quick reference for common development tasks.
π Full Documentation: See Initialization System for comprehensive technical details and architecture overview.
import { useService } from "solid-services";
import InitializationService from "~/services/initialization.service";
import { InitializationStep } from "~/types/initialization.types";
const initService = useService(InitializationService);
// Execute a step
await initService.executeStep(InitializationStep.MyStep, {
execute: async () => {
// Your initialization logic here
},
validate: async () => {
// Optional validation
return true;
}
});
// Wait for a step to complete
await initService.waitForStep(InitializationStep.ClientLoaded);
// Check step status
if (initService.isStepCompleted(InitializationStep.AudioService)) {
// Step is complete
}
src/types/initialization.types.ts
):
export enum InitializationStep {
// ... existing steps
MyNewStep = "my-new-step"
}
src/services/initialization.service.ts
):
const stepDependencies: Record<InitializationStep, InitializationStep[]> = {
// ... existing dependencies
[InitializationStep.MyNewStep]: [InitializationStep.AudioService],
};
src/routes/app-loading.tsx
):
await initializationService().executeStep(InitializationStep.MyNewStep, {
execute: async () => {
// Implementation
await myService().initialize();
},
validate: async () => {
return myService().isReady();
}
});
// Wait for multiple dependencies
await Promise.all([
initService.waitForStep(InitializationStep.ClientLoaded),
initService.waitForStep(InitializationStep.AudioService)
]);
// Wait with custom timeout
await initService.waitForStep(InitializationStep.CoreWasm, 60000); // 60 seconds
try {
await initService.executeStep(InitializationStep.MyStep, {
execute: async () => {
// This might fail
await riskyOperation();
}
});
} catch (error) {
// Handle initialization failure
console.error("Step failed:", error);
}
UserGesture
βββ CoreWasm
β βββ AppState
β β βββ WebsocketIdentity
β β β βββ WebsocketConnection
β β β β βββ WelcomeEvent
β β β β β βββ ClientLoaded
β β β β β β βββ AudioService β Audio service first
β β β β β β β βββ SynthEngine
β β β β β β β βββ ClientSocketId
β β β β β β β β βββ ClientAddedToSynth
β β β β β β β β βββ Soundfont β Soundfont after audio
β β β β β β β β βββ AppSettings
β β β β β β β β β βββ UsersService
β β β β β β β β β β βββ ChatService
β β β β β β β β β β β βββ RoomsService
β β β β β β β β β β β β βββ MonitorService
β β β β β β β β β β β β β βββ Complete
Most important step - prevents race conditions between synth engine and client socket ID:
await initializationService().executeStep(InitializationStep.ClientAddedToSynth, {
execute: async () => {
await raceTimeout(until(() => {
return appService().clientLoaded() &&
appService().getSocketID() &&
audioService().clientAdded();
}), DEFAULT_SERVICE_TIMEOUT, true, "Client never properly added to synth.");
},
validate: async () => {
return appService().clientLoaded() &&
!!appService().getSocketID() &&
audioService().clientAdded();
}
});
import { describe, it, expect, beforeEach } from 'vitest';
import { createRoot } from 'solid-js';
import InitializationService from '~/services/initialization.service';
import { InitializationStep } from '~/types/initialization.types';
describe('MyInitializationStep', () => {
let initService: any;
beforeEach(() => {
createRoot(() => {
initService = InitializationService();
});
});
it('should execute my step successfully', async () => {
const mockExecutor = {
execute: vi.fn().mockResolvedValue(undefined)
};
await initService.executeStep(InitializationStep.MyStep, mockExecutor);
expect(mockExecutor.execute).toHaveBeenCalled();
expect(initService.isStepCompleted(InitializationStep.MyStep)).toBe(true);
});
});
// In initialization service config
const config: InitializationConfig = {
enableLogging: true, // Enable detailed logging
defaultTimeout: 30000,
maxRetries: 3,
retryDelay: 1000
};
// Set up progress callback
initService.setProgressCallback((step, status, progress) => {
console.log(`Step ${step}: ${status} (${progress}%)`);
});
// Set up error callback
initService.setErrorCallback((step, error) => {
console.error(`Step ${step} failed:`, error);
});
const config: InitializationConfig = {
defaultTimeout: 30000, // 30 seconds
maxRetries: 3, // 3 retry attempts
retryDelay: 1000, // 1 second between retries
enableLogging: true // Debug logging enabled
};
// Set custom timeout for specific step
const state = initService.state();
const stepInfo = state.steps.get(InitializationStep.MyStep);
if (stepInfo) {
stepInfo.timeout = 60000; // 60 seconds
}
For complete technical details, see INITIALIZATION_ARCHITECTURE.md.