Browse Source

feat(scene): add transition transaction with rollback flow

Made-with: Cursor
master
npmrun 2 weeks ago
parent
commit
0848c35c2e
  1. 12
      src/scene/SceneManager.ts
  2. 29
      src/scene/TransitionTransaction.ts
  3. 24
      tests/kernel/transition-transaction.test.ts

12
src/scene/SceneManager.ts

@ -3,6 +3,7 @@ import Game from "@/core/Game";
import { logger } from "@/core/Logger";
import { SceneType } from "@/enums/SceneType";
import { BaseScene } from "./BaseScene";
import { TransitionTransaction } from "./TransitionTransaction";
import type { IBaseScene } from "./types";
// Extend the Container interface to allow setting and accessing _isHolderLast
@ -133,7 +134,8 @@ class SceneManager {
}
const target = this.getSceneOrThrow(name);
const transaction = new TransitionTransaction({
commit: async () => {
// 处理前一个场景
if (previous.type === SceneType.Normal) {
if (options?.isHolderLast) {
@ -189,8 +191,14 @@ class SceneManager {
// 触发回调
await this.emitStageChange(target, previous);
logger.debug(`SceneManager: changed from "${previous.name}" to "${name}"`);
},
rollback: async () => {
this._currentScene = previous;
},
});
await transaction.execute();
}
/** 获取已注册场景 */

29
src/scene/TransitionTransaction.ts

@ -0,0 +1,29 @@
type TransitionAction = () => Promise<void> | void;
export interface TransitionTransactionConfig {
prepare?: TransitionAction;
commit: TransitionAction;
rollback: TransitionAction;
}
export class TransitionTransaction {
private readonly prepare: TransitionAction;
private readonly commit: TransitionAction;
private readonly rollback: TransitionAction;
constructor(config: TransitionTransactionConfig) {
this.prepare = config.prepare ?? (() => {});
this.commit = config.commit;
this.rollback = config.rollback;
}
async execute(): Promise<void> {
try {
await this.prepare();
await this.commit();
} catch (error) {
await this.rollback();
throw error;
}
}
}

24
tests/kernel/transition-transaction.test.ts

@ -0,0 +1,24 @@
import { describe, expect, it, vi } from "vitest";
import { TransitionTransaction } from "@/scene/TransitionTransaction";
describe("TransitionTransaction", () => {
it("rolls back when prepare fails and never commits", async () => {
const prepareError = new Error("prepare failed");
const prepare = vi.fn(async () => {
throw prepareError;
});
const commit = vi.fn(async () => {});
const rollback = vi.fn(async () => {});
const transaction = new TransitionTransaction({
prepare,
commit,
rollback,
});
await expect(transaction.execute()).rejects.toThrowError("prepare failed");
expect(prepare).toHaveBeenCalledTimes(1);
expect(rollback).toHaveBeenCalledTimes(1);
expect(commit).not.toHaveBeenCalled();
});
});
Loading…
Cancel
Save