Loading recipe…
The minimum code
spinButton.addEventListener('click', async () => {
if (reelSet.isSpinning) {
// requestSkip is the safe variant: if the result hasn't arrived yet,
// it queues the slam-stop and fires it as soon as setResult() is in.
// Avoids the "user tapped before /api/spin returned" race.
reelSet.requestSkip();
return;
}
const promise = reelSet.spin();
const response = await fetch('/api/spin').then((r) => r.json());
reelSet.setResult(response.symbols);
const result = await promise;
if (result.wasSkipped) {
// skip paths can use shorter/no win animations
}
});
requestSkip() vs skip()
skip() | requestSkip() | |
|---|---|---|
| Result already set | slams now (and applies round-aware side effects. speed boost in standard mode, cascade auto-slam in tumble mode) | slams now |
| Result not set yet | THROWS. caller must catch | queues until result arrives, then slams |
| Caller responsibility | catch the throw and either route to requestSkip() or wait for setResult() | none |
For player-facing slam-stop UX, prefer requestSkip(). it removes the timing trap.
If you want the round-aware boost behaviour of skip() but also need to be tap-safe pre-setResult, use both:
spinButton.addEventListener('click', () => {
if (!reelSet.isSpinning) { /* start a new spin */ return; }
// skip() THROWS before setResult arrives. Route to requestSkip() in
// the catch so a tap during the server-wait window still queues the
// slam. and still picks up the boost / cascade auto-slam side effects
// when the result lands.
try { reelSet.skipSpin(); }
catch { reelSet.requestSkip(); }
});
What slam actually does
- Emits
skip:requested. - Force-completes every active spin phase (including GSAP tweens).
- Calls
reel.placeSymbols(targetRow)on every reel with the result fromsetResult(). - Snaps the reel container
yback to0. - Fires
spin:reelLandedper reel +spin:allLanded+spin:completewithwasSkipped: true. same lifecycle hooks as a normal landing.
Test it
import { createTestReelSet, captureEvents } from 'pixi-reels/testing';
const { reelSet, spinAndLand } = createTestReelSet({ reels: 5, visibleRows: 3, symbolIds: ['a','b','c'] });
const log = captureEvents(reelSet, ['skip:requested', 'skip:completed', 'spin:complete']);
const result = await spinAndLand([['a','a','a'],['b','b','b'],['c','c','c'],['a','b','c'],['c','b','a']]);
expect(result.wasSkipped).toBe(true);
expect(log.map((e) => e.event)).toEqual(['skip:requested','spin:complete','skip:completed']);