extra-pool 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +2 -1
  2. package/lib/index.js.map +1 -0
  3. package/lib/{es2015/instance.d.ts → instance.d.ts} +5 -3
  4. package/lib/{es2018/instance.js → instance.js} +35 -19
  5. package/lib/instance.js.map +1 -0
  6. package/lib/{es2015/pool.d.ts → pool.d.ts} +5 -3
  7. package/lib/{es2018/pool.js → pool.js} +46 -35
  8. package/lib/pool.js.map +1 -0
  9. package/package.json +26 -41
  10. package/src/index.ts +1 -0
  11. package/src/instance.ts +133 -0
  12. package/src/pool.ts +194 -0
  13. package/dist/es2015/index.min.mjs +0 -2
  14. package/dist/es2015/index.min.mjs.map +0 -1
  15. package/dist/es2015/index.mjs +0 -16672
  16. package/dist/es2015/index.mjs.map +0 -1
  17. package/dist/es2015/index.umd.js +0 -16683
  18. package/dist/es2015/index.umd.js.map +0 -1
  19. package/dist/es2015/index.umd.min.js +0 -2
  20. package/dist/es2015/index.umd.min.js.map +0 -1
  21. package/dist/es2018/index.min.mjs +0 -2
  22. package/dist/es2018/index.min.mjs.map +0 -1
  23. package/dist/es2018/index.mjs +0 -16648
  24. package/dist/es2018/index.mjs.map +0 -1
  25. package/dist/es2018/index.umd.js +0 -16659
  26. package/dist/es2018/index.umd.js.map +0 -1
  27. package/dist/es2018/index.umd.min.js +0 -2
  28. package/dist/es2018/index.umd.min.js.map +0 -1
  29. package/lib/es2015/index.js.map +0 -1
  30. package/lib/es2015/instance.js +0 -97
  31. package/lib/es2015/instance.js.map +0 -1
  32. package/lib/es2015/pool.js +0 -129
  33. package/lib/es2015/pool.js.map +0 -1
  34. package/lib/es2018/index.d.ts +0 -1
  35. package/lib/es2018/index.js +0 -18
  36. package/lib/es2018/index.js.map +0 -1
  37. package/lib/es2018/instance.d.ts +0 -19
  38. package/lib/es2018/instance.js.map +0 -1
  39. package/lib/es2018/pool.d.ts +0 -26
  40. package/lib/es2018/pool.js.map +0 -1
  41. /package/lib/{es2015/index.d.ts → index.d.ts} +0 -0
  42. /package/lib/{es2015/index.js → index.js} +0 -0
package/README.md CHANGED
@@ -31,9 +31,10 @@ await dbConnectionPool.destroy()
31
31
  interface IPoolOptions<T> {
32
32
  create: () => Awaitable<T>
33
33
  destroy?: (instance: T) => Awaitable<void>
34
- maxInstances: number
34
+ maxInstances?: number
35
35
  minInstances?: number = 0
36
36
  idleTimeout?: number = 0
37
+ concurrencyPerInstance? = 1
37
38
  }
38
39
 
39
40
  class Pool<T> {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAsB"}
@@ -8,10 +8,12 @@ export declare enum InstanceState {
8
8
  Destroyed = "destroyed"
9
9
  }
10
10
  export declare class Instance<T> {
11
- private destroyValue?;
11
+ private destroyInstance?;
12
12
  private fsm;
13
- readonly _value: Deferred<T>;
14
- constructor(createValue: () => Awaitable<T>, destroyValue?: ((value: T) => Awaitable<void>) | undefined);
13
+ private _users;
14
+ readonly _instance: Deferred<T>;
15
+ get users(): number;
16
+ constructor(createInstance: () => Awaitable<T>, destroyInstance?: ((value: T) => Awaitable<void>) | undefined);
15
17
  waitForCreated(): Promise<void>;
16
18
  getState(): InstanceState;
17
19
  use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Instance = exports.InstanceState = void 0;
4
4
  const prelude_1 = require("@blackglory/prelude");
5
5
  const extra_promise_1 = require("extra-promise");
6
- const structures_1 = require("@blackglory/structures");
6
+ const extra_fsm_1 = require("extra-fsm");
7
7
  const rxjs_1 = require("rxjs");
8
8
  const operators_1 = require("rxjs/operators");
9
9
  var InstanceState;
@@ -23,7 +23,7 @@ const instanceSchema = {
23
23
  destroy: InstanceState.Destroying
24
24
  },
25
25
  [InstanceState.Using]: {
26
- used: InstanceState.Idle
26
+ idle: InstanceState.Idle
27
27
  },
28
28
  [InstanceState.Destroying]: {
29
29
  destroyed: InstanceState.Destroyed
@@ -31,49 +31,65 @@ const instanceSchema = {
31
31
  [InstanceState.Destroyed]: {}
32
32
  };
33
33
  class Instance {
34
- constructor(createValue, destroyValue) {
35
- this.destroyValue = destroyValue;
36
- this._value = new extra_promise_1.Deferred();
37
- this.fsm = new structures_1.ObservableFiniteStateMachine(instanceSchema, InstanceState.Creating);
34
+ get users() {
35
+ return this._users;
36
+ }
37
+ constructor(createInstance, destroyInstance) {
38
+ this.destroyInstance = destroyInstance;
39
+ this._users = 0;
40
+ this._instance = new extra_promise_1.Deferred();
41
+ this.fsm = new extra_fsm_1.ObservableFiniteStateMachine(instanceSchema, InstanceState.Creating);
38
42
  (0, prelude_1.go)(async () => {
39
43
  try {
40
- const val = await createValue();
41
- this._value.resolve(val);
44
+ const instance = await createInstance();
42
45
  this.fsm.send('created');
46
+ this._instance.resolve(instance);
43
47
  }
44
48
  catch (e) {
45
- this._value.reject(e);
49
+ this._instance.reject(e);
46
50
  }
47
51
  });
48
52
  }
49
53
  async waitForCreated() {
50
- await this._value;
54
+ await this._instance;
51
55
  }
52
56
  getState() {
53
57
  return this.fsm.state;
54
58
  }
55
59
  async use(fn) {
56
- this.fsm.send('use');
57
- const value = await this._value;
60
+ (0, prelude_1.assert)(this.fsm.state !== InstanceState.Destroying &&
61
+ this.fsm.state !== InstanceState.Destroyed, 'The instance is not available');
62
+ this._users++;
63
+ if (this.fsm.state === InstanceState.Creating) {
64
+ await this._instance;
65
+ }
66
+ if (this.fsm.state === InstanceState.Idle) {
67
+ this.fsm.send('use');
68
+ }
69
+ (0, prelude_1.assert)(this.fsm.state === InstanceState.Using, 'The instance state should be using');
70
+ const instance = await this._instance;
58
71
  try {
59
- return await fn(value);
72
+ const result = await fn(instance);
73
+ return result;
60
74
  }
61
75
  finally {
62
- this.fsm.send('used');
76
+ if ((--this._users) === 0) {
77
+ this.fsm.send('idle');
78
+ }
63
79
  }
64
80
  }
65
81
  async destroy() {
66
82
  var _a;
67
- if (this.fsm.matches(InstanceState.Creating) ||
68
- this.fsm.matches(InstanceState.Using)) {
83
+ if (this.fsm.state === InstanceState.Creating ||
84
+ this.fsm.state === InstanceState.Using) {
69
85
  await (0, rxjs_1.firstValueFrom)(this.fsm.observeStateChanges().pipe((0, operators_1.filter)(state => state.newState === InstanceState.Idle)));
70
86
  }
71
- if (this.fsm.matches(InstanceState.Idle)) {
87
+ if (this.fsm.state === InstanceState.Idle) {
72
88
  this.fsm.send('destroy');
73
- await ((_a = this.destroyValue) === null || _a === void 0 ? void 0 : _a.call(this, await this._value));
89
+ await ((_a = this.destroyInstance) === null || _a === void 0 ? void 0 : _a.call(this, await this._instance));
74
90
  this.fsm.send('destroyed');
75
91
  }
76
- else if (this.fsm.matches(InstanceState.Destroying)) {
92
+ else if (this.fsm.state === InstanceState.Destroying) {
77
93
  await (0, rxjs_1.firstValueFrom)(this.fsm.observeStateChanges().pipe((0, operators_1.filter)(state => state.newState === InstanceState.Destroyed)));
78
94
  }
79
95
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
@@ -6,16 +6,18 @@ interface IPoolOptions<T> {
6
6
  maxInstances?: number;
7
7
  minInstances?: number;
8
8
  idleTimeout?: number;
9
+ concurrencyPerInstance?: number;
9
10
  }
10
11
  export declare class Pool<T> {
11
- private createValue;
12
- private destroyValue?;
12
+ private createInstance;
13
+ private destroyInstance?;
13
14
  private fsm;
15
+ private waitingUsers;
14
16
  private items;
15
- private userDeferredQueue;
16
17
  private maxInstances;
17
18
  private minInstances;
18
19
  private idleTimeout;
20
+ private concurrencyPerInstance;
19
21
  get size(): number;
20
22
  constructor(options: IPoolOptions<T>);
21
23
  use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
@@ -2,11 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UnavailablePool = exports.Pool = void 0;
4
4
  const errors_1 = require("@blackglory/errors");
5
+ const prelude_1 = require("@blackglory/prelude");
5
6
  const structures_1 = require("@blackglory/structures");
7
+ const extra_fsm_1 = require("extra-fsm");
6
8
  const extra_promise_1 = require("extra-promise");
7
9
  const iterable_operator_1 = require("iterable-operator");
8
- const instance_1 = require("./instance");
9
10
  const extra_timers_1 = require("extra-timers");
11
+ const instance_1 = require("./instance");
10
12
  var PoolState;
11
13
  (function (PoolState) {
12
14
  PoolState["Running"] = "running";
@@ -23,70 +25,79 @@ const poolSchema = {
23
25
  [PoolState.Destroyed]: {}
24
26
  };
25
27
  class Pool {
28
+ get size() {
29
+ return this.items.size;
30
+ }
26
31
  constructor(options) {
27
- var _a, _b, _c;
28
- this.fsm = new structures_1.FiniteStateMachine(poolSchema, PoolState.Running);
32
+ var _a, _b, _c, _d;
33
+ this.fsm = new extra_fsm_1.FiniteStateMachine(poolSchema, PoolState.Running);
34
+ this.waitingUsers = new structures_1.Queue();
29
35
  this.items = new Set();
30
- this.userDeferredQueue = new structures_1.Queue();
31
- this.createValue = options.create;
32
- this.destroyValue = options.destroy;
36
+ this.createInstance = options.create;
37
+ this.destroyInstance = options.destroy;
33
38
  this.maxInstances = (_a = options.maxInstances) !== null && _a !== void 0 ? _a : Infinity;
34
39
  this.minInstances = (_b = options.minInstances) !== null && _b !== void 0 ? _b : 0;
35
40
  this.idleTimeout = (_c = options.idleTimeout) !== null && _c !== void 0 ? _c : 0;
36
- }
37
- get size() {
38
- return this.items.size;
41
+ this.concurrencyPerInstance = (_d = options.concurrencyPerInstance) !== null && _d !== void 0 ? _d : 1;
39
42
  }
40
43
  async use(fn) {
41
44
  const self = this;
42
- const item = (0, iterable_operator_1.find)(this.items, instance => !instance.using);
45
+ const item = (0, prelude_1.go)(() => {
46
+ const candidateItems = (0, iterable_operator_1.toArray)((0, iterable_operator_1.filter)(this.items, item => item.instance.users < this.concurrencyPerInstance));
47
+ if (candidateItems.length) {
48
+ return candidateItems.reduce((previous, current) => {
49
+ if (current.instance.users < previous.instance.users) {
50
+ return current;
51
+ }
52
+ else {
53
+ return previous;
54
+ }
55
+ });
56
+ }
57
+ });
43
58
  if (item) {
44
- return await use(item);
59
+ return await useItem(item);
45
60
  }
46
61
  else {
47
62
  if (this.items.size < this.maxInstances) {
48
- const instance = new instance_1.Instance(this.createValue, this.destroyValue);
49
- const item = {
50
- instance,
51
- using: true
52
- };
63
+ const instance = new instance_1.Instance(this.createInstance, this.destroyInstance);
64
+ const item = { instance };
53
65
  this.items.add(item);
54
- return await use(item);
66
+ return await useItem(item);
55
67
  }
56
68
  else {
57
- const deferred = new extra_promise_1.Deferred();
58
- this.userDeferredQueue.enqueue(deferred);
59
- const item = await deferred;
60
- return await use(item);
69
+ const waitingUser = new extra_promise_1.Deferred();
70
+ this.waitingUsers.enqueue(waitingUser);
71
+ const item = await waitingUser;
72
+ return await useItem(item);
61
73
  }
62
74
  }
63
- async function use(item) {
64
- item.using = true;
65
- if (item.cancelDeletion) {
66
- item.cancelDeletion();
67
- delete item.cancelDeletion;
75
+ async function useItem(item) {
76
+ if (item.cancelScheduledDeletion) {
77
+ item.cancelScheduledDeletion();
78
+ delete item.cancelScheduledDeletion;
68
79
  }
69
- await item.instance.waitForCreated();
70
80
  try {
71
81
  return await item.instance.use(fn);
72
82
  }
73
83
  finally {
74
- if (self.userDeferredQueue.size > 0) {
75
- self.userDeferredQueue.dequeue().resolve(item);
84
+ const waitingUser = self.waitingUsers.dequeue();
85
+ if (waitingUser) {
86
+ waitingUser.resolve(item);
76
87
  }
77
88
  else {
78
- item.using = false;
79
- if (self.items.size > self.minInstances) {
89
+ if (item.instance.users === 0 &&
90
+ self.items.size > self.minInstances) {
80
91
  if (self.idleTimeout > 0) {
81
- item.cancelDeletion = (0, extra_timers_1.setTimeout)(self.idleTimeout, removeItem);
92
+ item.cancelScheduledDeletion = (0, extra_timers_1.setTimeout)(self.idleTimeout, deleteInstance);
82
93
  }
83
94
  else {
84
- await removeItem();
95
+ await deleteInstance();
85
96
  }
86
97
  }
87
98
  }
88
99
  }
89
- async function removeItem() {
100
+ async function deleteInstance() {
90
101
  self.items.delete(item);
91
102
  await item.instance.destroy();
92
103
  }
@@ -99,7 +110,7 @@ class Pool {
99
110
  }
100
111
  this.items.clear();
101
112
  let deferred;
102
- while (deferred = this.userDeferredQueue.dequeue()) {
113
+ while (deferred = this.waitingUsers.dequeue()) {
103
114
  deferred.reject(new UnavailablePool());
104
115
  }
105
116
  this.fsm.send('destroyed');
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extra-pool",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A library that helps you create object/thread/connection pools",
5
5
  "keywords": [
6
6
  "pool",
@@ -10,10 +10,10 @@
10
10
  ],
11
11
  "files": [
12
12
  "lib",
13
- "dist"
13
+ "src"
14
14
  ],
15
- "main": "lib/es2018/index.js",
16
- "types": "lib/es2018/index.d.ts",
15
+ "main": "lib/index.js",
16
+ "types": "lib/index.d.ts",
17
17
  "sideEffects": false,
18
18
  "repository": "git@github.com:BlackGlory/extra-pool.git",
19
19
  "author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
@@ -24,18 +24,11 @@
24
24
  "test": "jest --runInBand --config jest.config.js",
25
25
  "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
26
26
  "test:coverage": "jest --coverage --config jest.config.js",
27
- "prepublishOnly": "run-s clean build bundle",
28
- "clean": "run-p clean:*",
29
- "clean:build": "rimraf lib",
30
- "clean:bundle": "rimraf dist",
31
- "build": "run-p build:*",
32
- "build:es2015": "run-s build:es2015:*",
33
- "build:es2015:compile": "tsc --project tsconfig.build.json --module commonjs --target es2015 --outDir lib/es2015",
34
- "build:es2015:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib/es2015",
35
- "build:es2018": "run-s build:es2018:*",
36
- "build:es2018:compile": "tsc --project tsconfig.build.json --module commonjs --target es2018 --outDir lib/es2018",
37
- "build:es2018:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib/es2018",
38
- "bundle": "rollup --config rollup.config.js",
27
+ "prepublishOnly": "run-s clean build",
28
+ "clean": "rimraf lib",
29
+ "build": "run-s build:*",
30
+ "build:compile": "tsc --project tsconfig.build.json --module commonjs --target es2018 --outDir lib",
31
+ "build:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib",
39
32
  "release": "standard-version"
40
33
  },
41
34
  "husky": {
@@ -45,39 +38,31 @@
45
38
  }
46
39
  },
47
40
  "devDependencies": {
48
- "@blackglory/jest-matchers": "^0.3.1",
49
- "@commitlint/cli": "^17.0.3",
50
- "@commitlint/config-conventional": "^17.0.3",
51
- "@rollup/plugin-commonjs": "^22.0.2",
52
- "@rollup/plugin-json": "^4.1.0",
53
- "@rollup/plugin-node-resolve": "^13.1.3",
54
- "@rollup/plugin-replace": "^4.0.0",
55
- "@rollup/plugin-typescript": "^8.4.0",
56
- "@types/jest": "^27.4.1",
57
- "@typescript-eslint/eslint-plugin": "^5.35.1",
58
- "@typescript-eslint/parser": "^5.35.1",
59
- "eslint": "^8.22.0",
41
+ "@commitlint/cli": "^17.3.0",
42
+ "@commitlint/config-conventional": "^17.3.0",
43
+ "@types/jest": "^29.2.3",
44
+ "@typescript-eslint/eslint-plugin": "^5.44.0",
45
+ "@typescript-eslint/parser": "^5.44.0",
46
+ "eslint": "^8.28.0",
60
47
  "husky": "^4.3.8",
61
- "jest": "^27.5.1",
48
+ "jest": "^29.3.1",
62
49
  "npm-run-all": "^4.1.5",
63
50
  "rimraf": "^3.0.2",
64
- "rollup": "^2.78.1",
65
- "rollup-plugin-analyzer": "^4.0.0",
66
- "rollup-plugin-terser": "^7.0.2",
67
51
  "standard-version": "^9.5.0",
68
- "ts-jest": "^27.1.4",
52
+ "ts-jest": "^29.0.3",
69
53
  "tscpaths": "^0.0.9",
70
- "tslib": "^2.3.1",
71
- "typescript": "^4.8.2",
54
+ "tslib": "^2.4.1",
55
+ "typescript": "^4.9.3",
72
56
  "yarn-deduplicate": "^6.0.0"
73
57
  },
74
58
  "dependencies": {
75
- "@blackglory/errors": "^2.2.2",
76
- "@blackglory/prelude": "^0.1.4",
77
- "@blackglory/structures": "^0.10.14",
78
- "extra-promise": "^3.2.0",
59
+ "@blackglory/errors": "^2.3.0",
60
+ "@blackglory/prelude": "^0.1.8",
61
+ "@blackglory/structures": "^0.11.4",
62
+ "extra-fsm": "^0.1.1",
63
+ "extra-promise": "^4.4.0",
79
64
  "extra-timers": "^0.2.5",
80
- "iterable-operator": "^1.2.1",
81
- "rxjs": "^7.5.6"
65
+ "iterable-operator": "^2.5.0",
66
+ "rxjs": "^7.5.7"
82
67
  }
83
68
  }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './pool'
@@ -0,0 +1,133 @@
1
+ import { go, assert, Awaitable } from '@blackglory/prelude'
2
+ import { Deferred } from 'extra-promise'
3
+ import { ObservableFiniteStateMachine } from 'extra-fsm'
4
+ import { firstValueFrom } from 'rxjs'
5
+ import { filter } from 'rxjs/operators'
6
+
7
+ export enum InstanceState {
8
+ Creating = 'creating'
9
+ , Idle = 'idle'
10
+ , Using = 'using'
11
+ , Destroying = 'destroying'
12
+ , Destroyed = 'destroyed'
13
+ }
14
+
15
+ type InstanceEvent =
16
+ | 'created'
17
+ | 'use'
18
+ | 'idle'
19
+ | 'destroy'
20
+ | 'destroyed'
21
+
22
+ const instanceSchema = {
23
+ [InstanceState.Creating]: {
24
+ created: InstanceState.Idle
25
+ }
26
+ , [InstanceState.Idle]: {
27
+ use: InstanceState.Using
28
+ , destroy: InstanceState.Destroying
29
+ }
30
+ , [InstanceState.Using]: {
31
+ idle: InstanceState.Idle
32
+ }
33
+ , [InstanceState.Destroying]: {
34
+ destroyed: InstanceState.Destroyed
35
+ }
36
+ , [InstanceState.Destroyed]: {}
37
+ }
38
+
39
+ export class Instance<T> {
40
+ private fsm: ObservableFiniteStateMachine<InstanceState, InstanceEvent>
41
+ private _users = 0
42
+ readonly _instance: Deferred<T>
43
+
44
+ get users(): number {
45
+ return this._users
46
+ }
47
+
48
+ constructor(
49
+ createInstance: () => Awaitable<T>
50
+ , private destroyInstance?: (value: T) => Awaitable<void>
51
+ ) {
52
+ this._instance = new Deferred<T>()
53
+ this.fsm = new ObservableFiniteStateMachine<InstanceState, InstanceEvent>(
54
+ instanceSchema
55
+ , InstanceState.Creating
56
+ )
57
+
58
+ go(async () => {
59
+ try {
60
+ const instance = await createInstance()
61
+ this.fsm.send('created')
62
+ this._instance.resolve(instance)
63
+ } catch (e) {
64
+ this._instance.reject(e)
65
+ }
66
+ })
67
+ }
68
+
69
+ async waitForCreated(): Promise<void> {
70
+ await this._instance
71
+ }
72
+
73
+ getState(): InstanceState {
74
+ return this.fsm.state
75
+ }
76
+
77
+ async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
78
+ // 不要尝试将此处的代码改编成switch管道, 很难正确编写.
79
+
80
+ assert(
81
+ this.fsm.state !== InstanceState.Destroying &&
82
+ this.fsm.state !== InstanceState.Destroyed
83
+ , 'The instance is not available'
84
+ )
85
+
86
+ this._users++
87
+
88
+ if (this.fsm.state === InstanceState.Creating) {
89
+ await this._instance
90
+ }
91
+
92
+ if (this.fsm.state === InstanceState.Idle) {
93
+ this.fsm.send('use')
94
+ }
95
+
96
+ assert(this.fsm.state === InstanceState.Using, 'The instance state should be using')
97
+ const instance = await this._instance
98
+ try {
99
+ const result = await fn(instance)
100
+ return result
101
+ } finally {
102
+ if ((--this._users) === 0) {
103
+ this.fsm.send('idle')
104
+ }
105
+ }
106
+ }
107
+
108
+ async destroy(): Promise<void> {
109
+ if (
110
+ this.fsm.state === InstanceState.Creating ||
111
+ this.fsm.state === InstanceState.Using
112
+ ) {
113
+ await firstValueFrom(
114
+ this.fsm.observeStateChanges().pipe(
115
+ filter(state => state.newState === InstanceState.Idle)
116
+ )
117
+ )
118
+ }
119
+
120
+ if (this.fsm.state === InstanceState.Idle) {
121
+ this.fsm.send('destroy')
122
+ // 如果destroyed过程报错, 则程序崩溃, 这是预期行为.
123
+ await this.destroyInstance?.(await this._instance)
124
+ this.fsm.send('destroyed')
125
+ } else if (this.fsm.state === InstanceState.Destroying) {
126
+ await firstValueFrom(
127
+ this.fsm.observeStateChanges().pipe(
128
+ filter(state => state.newState === InstanceState.Destroyed)
129
+ )
130
+ )
131
+ }
132
+ }
133
+ }