((resolve, reject) => {
+ let killConn: (() => any) | null | undefined = undefined;
+ let error: any;
+ let result: any;
+ let state = 0; // 0 = not started; 1 = started; 2 = done
+ const onDisconnected = () => {
+ killConn = null; // before resolve, set killConn to null
+ if (error) {
+ reject(error);
+ } else if (state !== 2) {
+ reject(new Error("GM.runExclusive: Incomplete Action"));
+ } else {
+ resolve(result);
+ }
+ result = null; // GC
+ error = null; // GC
+ };
+ const onStart = async (con: MessageConnect) => {
+ if (killConn === null || state > 0) {
+ // already resolved (unexpected or by timeout)
+ con.disconnect();
+ return;
+ }
+ state = 1;
+ try {
+ result = await cb();
+ } catch (e) {
+ error = e;
+ }
+ state = 2;
+ con.sendMessage({
+ action: "done",
+ data: error ? false : typeof result,
+ });
con.disconnect();
- return;
+ onDisconnected(); // in case .disconnect() not working
+ };
+ this.connect("runExclusive", [key]).then((con) => {
+ if (killConn === null || state > 0) {
+ // already resolved (unexpected or by timeout)
+ con.disconnect();
+ return;
+ }
+ killConn = () => {
+ con.disconnect();
+ };
+ con.onDisconnect(onDisconnected);
+ con.onMessage((msg) => {
+ switch (msg.action) {
+ case "start":
+ onStart(con);
+ break;
+ }
+ });
+ });
+ if (timeout > 0) {
+ setTimeout(() => {
+ if (killConn === null || state > 0) return; // 执行开始了就不进行 timeout 操作
+ error = new Error("GM.runExclusive: Timeout Error");
+ killConn?.();
+ onDisconnected(); // in case .disconnect() not working
+ }, timeout);
}
- state = 1;
+ });
+
+ return new Promise((resolve, reject) => {
+ stackAsyncTask(`runExclusive::${key}`, async () => {
try {
- result = await cb();
+ resolve(await taskAsync());
} catch (e) {
- error = e;
- }
- state = 2;
- con.sendMessage({
- action: "done",
- data: error ? false : typeof result,
- });
- con.disconnect();
- onDisconnected(); // in case .disconnect() not working
- };
- this.connect("runExclusive", [key]).then((con) => {
- if (killConn === null || state > 0) {
- // already resolved (unexpected or by timeout)
- con.disconnect();
- return;
+ reject(e);
}
- killConn = () => {
- con.disconnect();
- };
- con.onDisconnect(onDisconnected);
- con.onMessage((msg) => {
- switch (msg.action) {
- case "start":
- onStart(con);
- break;
- }
- });
});
- if (timeout > 0) {
- setTimeout(() => {
- if (killConn === null || state > 0) return; // 执行开始了就不进行 timeout 操作
- error = new Error("GM.runExclusive: Timeout Error");
- killConn?.();
- onDisconnected(); // in case .disconnect() not working
- }, timeout);
- }
});
}
}
From c7bb5b4a3ba6c5dd307d4511ed423d51c7341bb4 Mon Sep 17 00:00:00 2001
From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Date: Fri, 1 May 2026 19:34:51 +0900
Subject: [PATCH 5/5] `GM.runExclusive` -> `GM.takeTurn`
---
example/{gm_run_exclusive.js => gm_take_turn.js} | 16 ++++++++--------
src/app/service/content/gm_api/gm_api.ts | 14 +++++++-------
src/app/service/service_worker/gm_api/gm_api.ts | 4 ++--
src/template/scriptcat.d.tpl | 2 +-
src/types/scriptcat.d.ts | 2 +-
5 files changed, 19 insertions(+), 19 deletions(-)
rename example/{gm_run_exclusive.js => gm_take_turn.js} (92%)
diff --git a/example/gm_run_exclusive.js b/example/gm_take_turn.js
similarity index 92%
rename from example/gm_run_exclusive.js
rename to example/gm_take_turn.js
index 3ffdf9456..920d073f1 100644
--- a/example/gm_run_exclusive.js
+++ b/example/gm_take_turn.js
@@ -1,9 +1,9 @@
// ==UserScript==
-// @name GM.runExclusive Demo
+// @name GM.takeTurn Demo
// @namespace https://docs.scriptcat.org/
// @version 0.1.2
-// @match https://example.com/*?runExclusive*
-// @grant GM.runExclusive
+// @match https://example.com/*?takeTurn*
+// @grant GM.takeTurn
// @grant GM.setValue
// @grant GM.getValue
// @run-at document-start
@@ -13,7 +13,7 @@
(async function () {
'use strict';
- const delayMatch = location.href.match(/runExclusive(\d+)_(\d*)/);
+ const delayMatch = location.href.match(/takeTurn(\d+)_(\d*)/);
const timeDelay = delayMatch ? +delayMatch[1] : 0;
const timeoutValue = (delayMatch ? +delayMatch[2] : 0) || -1;
const isWorker = !!timeDelay;
@@ -68,7 +68,7 @@
if (!isWorker) {
panel.style.width = "480px";
panel.innerHTML = `
- GM.runExclusive Demo
+ GM.takeTurn Demo
Pick worker durations (ms):
{
const iframe = document.createElement('iframe');
- iframe.src = `${location.pathname}?runExclusive${delay}_${timeoutQ}`;
+ iframe.src = `${location.pathname}?takeTurn${delay}_${timeoutQ}`;
iframe.style.width = '100%';
iframe.style.height = '160px';
iframe.style.border = '1px solid #444';
@@ -120,7 +120,7 @@
if (e.data?.type !== 'close-worker') return;
const iframes = iframeContainer.querySelectorAll('iframe');
for (const iframe of iframes) {
- if (iframe.src.includes(`runExclusive${e.data.delay}_`)) {
+ if (iframe.src.includes(`takeTurn${e.data.delay}_`)) {
iframe.remove();
log(`Closed worker ${e.data.delay}ms`, '#ff9800');
return;
@@ -157,7 +157,7 @@
const startWait = performance.now();
try {
- const result = await GM.runExclusive('demo-lock-key', async () => {
+ const result = await GM.takeTurn('demo-lock-key', async () => {
const waited = Math.round(performance.now() - startWait);
const order = (await GM.getValue('order')) + 1;
diff --git a/src/app/service/content/gm_api/gm_api.ts b/src/app/service/content/gm_api/gm_api.ts
index 09ec2d8cd..dd6478ba7 100644
--- a/src/app/service/content/gm_api/gm_api.ts
+++ b/src/app/service/content/gm_api/gm_api.ts
@@ -1544,11 +1544,11 @@ export default class GMApi extends GM_Base {
return this.loadScriptPromise;
}
- @GMContext.API({ alias: "GM_runExclusive" })
- ["GM.runExclusive"](lockKey: string, cb: () => T | PromiseLike, timeout: number = -1): Promise {
+ @GMContext.API({ alias: "GM_takeTurn" })
+ ["GM.takeTurn"](lockKey: string, cb: () => T | PromiseLike, timeout: number = -1): Promise {
lockKey = `${lockKey}`; // 转化为字串
if (!lockKey || !this.scriptRes) {
- throw new Error("GM.runExclusive: Invalid Calling");
+ throw new Error("GM.takeTurn: Invalid Calling");
}
const key = `${getStorageName(this.scriptRes).replace(/:/g, ":_")}::${lockKey.replace(/:/g, ":_")}`;
@@ -1563,7 +1563,7 @@ export default class GMApi extends GM_Base {
if (error) {
reject(error);
} else if (state !== 2) {
- reject(new Error("GM.runExclusive: Incomplete Action"));
+ reject(new Error("GM.takeTurn: Incomplete Action"));
} else {
resolve(result);
}
@@ -1590,7 +1590,7 @@ export default class GMApi extends GM_Base {
con.disconnect();
onDisconnected(); // in case .disconnect() not working
};
- this.connect("runExclusive", [key]).then((con) => {
+ this.connect("takeTurn", [key]).then((con) => {
if (killConn === null || state > 0) {
// already resolved (unexpected or by timeout)
con.disconnect();
@@ -1611,7 +1611,7 @@ export default class GMApi extends GM_Base {
if (timeout > 0) {
setTimeout(() => {
if (killConn === null || state > 0) return; // 执行开始了就不进行 timeout 操作
- error = new Error("GM.runExclusive: Timeout Error");
+ error = new Error("GM.takeTurn: Timeout Error");
killConn?.();
onDisconnected(); // in case .disconnect() not working
}, timeout);
@@ -1619,7 +1619,7 @@ export default class GMApi extends GM_Base {
});
return new Promise((resolve, reject) => {
- stackAsyncTask(`runExclusive::${key}`, async () => {
+ stackAsyncTask(`takeTurn::${key}`, async () => {
try {
resolve(await taskAsync());
} catch (e) {
diff --git a/src/app/service/service_worker/gm_api/gm_api.ts b/src/app/service/service_worker/gm_api/gm_api.ts
index 70698f5b4..280fd0083 100644
--- a/src/app/service/service_worker/gm_api/gm_api.ts
+++ b/src/app/service/service_worker/gm_api/gm_api.ts
@@ -1388,8 +1388,8 @@ export default class GMApi {
}
}
- @PermissionVerify.API({ link: ["GM.runExclusive", "GM_runExclusive"] })
- runExclusive(request: GMApiRequest<[string]>, sender: IGetSender) {
+ @PermissionVerify.API({ link: ["GM.takeTurn", "GM_takeTurn"] })
+ takeTurn(request: GMApiRequest<[string]>, sender: IGetSender) {
if (!request.params || request.params.length < 1) {
throw new Error("param is failed");
}
diff --git a/src/template/scriptcat.d.tpl b/src/template/scriptcat.d.tpl
index 425bd4954..a1c7b96ab 100644
--- a/src/template/scriptcat.d.tpl
+++ b/src/template/scriptcat.d.tpl
@@ -306,7 +306,7 @@ declare const GM: {
cookie(action: GMTypes.CookieAction, details: GMTypes.CookieDetails): Promise;
/** cross-context exclusive execution */
- runExclusive(key: string, callback: () => T | PromiseLike, timeout?: number): Promise;
+ takeTurn(key: string, callback: () => T | PromiseLike, timeout?: number): Promise;
};
/**
diff --git a/src/types/scriptcat.d.ts b/src/types/scriptcat.d.ts
index 2ea00d095..b6f5e7b09 100644
--- a/src/types/scriptcat.d.ts
+++ b/src/types/scriptcat.d.ts
@@ -306,7 +306,7 @@ declare const GM: {
cookie(action: GMTypes.CookieAction, details: GMTypes.CookieDetails): Promise;
/** cross-context exclusive execution */
- runExclusive(key: string, callback: () => T | PromiseLike, timeout?: number): Promise;
+ takeTurn(key: string, callback: () => T | PromiseLike, timeout?: number): Promise;
};
/**