Skip to content

Conversation

@cyfung1031
Copy link
Collaborator

@cyfung1031 cyfung1031 commented Nov 14, 2025

概述 Descriptions

依存: #949

变更内容 Changes

截图 Screenshots


测试代码(一):

修改后:GM.listValues() 能在冲突中取得最新,而且不会因本地缓存与valueUpdate冲突而造成次序不一
useAsync 改为 false 的话就能看 GM_xxxx 的结果 )

// ==UserScript==
// @name         测试 GM.getValue 能否取得最新值
// @namespace    yourname.scripts
// @version      0.1.0
// @description  把当前网页URL保存到存储列表中
// @author       You
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.deleteValue
// @grant        GM.listValues
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// ==/UserScript==

(function () {
    'use strict';
    let useAsync = true;
    const rid = `${(Math.floor(Math.random() * 5000) + 4000).toString(36)}`;

    const trigger = async () => {
        console.log(`[${rid}]`,"trigger in " + location.href, "current time = " + Date.now());
        useAsync ? action2() : action1();
    }
    let k = 0;
    const action1 = async () => {
        ++k;
        GM_setValue(`${"list_"}${rid}${k}`, Date.now());
        console.log(`[${rid}]`, GM_listValues());
    };

    const action2 = async () => {
        ++k;
        await GM.setValue(`${"list_"}${rid}${k}`, Date.now());
        console.log(`[${rid}]`, await GM.listValues());
    };

    const wins = [];
    let tc0;
    window.addEventListener("message", (e) => {
        if (e.data && typeof e.data === "object" && e.data?.test_call_id && e.data?.type === "response_tc") {
            const tc1 = e.data.test_call_id;
            if (tc1 === tc0) {
                trigger();
            }
        }
    });
    if (location.search.startsWith("?test_call=") && top !== window) {
        const usp = new URLSearchParams(location.search);
        const tc = usp.get("test_call");
        if (tc) {
            tc0 = tc;
            window.top.postMessage({ type: "done_iframe_tc", test_call_id: tc }, "*");
        }
    } else if (window.location.href.includes("example.com") && top === window) {
        const test_call_id = `tc${Date.now()}_${Math.random()}`;
        tc0 = test_call_id;
        let q = 1;
        let ec = 0;
        const doFunc = async (elements) => {
            console.log(`[${rid}]`,"---- ADD SOME INFO... ------");
            await (useAsync ? action2() : action1());
            await (useAsync ? action2() : action1());
            await (useAsync ? action2() : action1());
            console.log(`[${rid}]`,"---------------------------");
            setTimeout(() => {
                console.log(`[${rid}]`,"do trigger");
                for (const iframe of elements) {
                    try {
                        iframe.contentWindow.postMessage({ type: "response_tc", test_call_id },
                            "*"
                        )
                    } catch (e) {
                        // ignored
                    }
                }
                window.postMessage({ type: "response_tc", test_call_id }, "*");

                setTimeout(async () => {
                     console.log(`[${rid}]`,"final list", await GM.listValues());
                }, 1500)
            }, 1500);
        }
        window.addEventListener("message", (e) => {
            if (e.data && typeof e.data === "object" && e.data?.test_call_id && e.data?.type === "done_iframe_tc") {
                const tc = e.data.test_call_id;
                if (tc === test_call_id) {
                    const elements = document.querySelectorAll("iframe.tt0011");
                    // wins.push([e.source, e.origin]);
                    wins.push(1);
                    if (wins.length === elements.length && elements.length === ec && q) {
                        q = 0;
                        doFunc(elements);
                    }
                }
            }
        });
        
        const makeIframe = () => {
            const elm = document.body.appendChild(document.createElement("iframe"));
            elm.classList.add("tt0011");
            return elm;
        }

        ec = 3;
        setTimeout(()=>{

            makeIframe().src = `https://example.com/?test_call=${test_call_id}`;
        }, 1800);

        setTimeout(()=>{

            makeIframe().src = `https://example.com/?test_call=${test_call_id}`;
        }, 2400);

        setTimeout(() => {
            makeIframe().src = `https://example.com/?test_call=${test_call_id}`;
        }, 3200);
    }
})();

测试代码(二):

(有GM_lock 做时间控制)修改后 GM.getValue 的列表新增没问题

// ==UserScript==
// @name         Example Script for GM_lock
// @namespace    yourname.scripts
// @version      0.1
// @description  把当前网页URL保存到存储列表中
// @author       You
// @match        *://*/*
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.setValues
// @grant        GM.deleteValue
// @grant        GM.deleteValues
// @grant        GM.listValues
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// @require      https://update.greasyfork.org/scripts/554436/1692608/GM_lock.js
// @noframes
// ==/UserScript==

/* global GM_lock */
(function () {
  'use strict';
  GM_lock("lock_urls", async () => {
    console.log("开始", Date.now(), performance.now());
    // 等一下这个页面SC的缓存更新
    // await new Promise(resolve => setTimeout(resolve, 50));
    // 从存储中读取已有的列表
    let list = await GM.getValue('list', []); // 设置默认值为空数组
    // 如果当前URL不在列表中,就添加进去
    console.log("初始列表:", list.slice());
    if (!list.includes(location.href)) {
      list.push(location.href);
      await GM.setValue('list', list);
      console.log('✅ 已保存此页面到列表:', location.href);
    } else {
      console.log('ℹ️ 当前页面已在列表中');
    }
    // 可选:在控制台查看当前列表
    console.log('当前列表:', list.slice());
    // 等一下其他页面SC的缓存更新
    // await new Promise(resolve => setTimeout(resolve, 50));
    console.log("结束", Date.now(), performance.now());
  });
})();

@cyfung1031 cyfung1031 changed the title 異步 getValue/getValues/listValues 相关修改 異步 getValue/getValues/listValues 相关修改 & 修正 deleteValue/deleteValues 无法执行问题 Nov 14, 2025
@CodFrm
Copy link
Member

CodFrm commented Nov 15, 2025

怎么都到这个分支去了 develop/raw-message

@cyfung1031
Copy link
Collaborator Author

怎么都到这个分支去了 develop/raw-message

因为两边的 commit 互相影响
但处理的内容不一样,写在同一PR又太多又乱

@CodFrm CodFrm changed the base branch from develop/raw-message to release/v1.3 November 15, 2025 14:10
@cyfung1031 cyfung1031 changed the title 異步 getValue/getValues/listValues 相关修改 & 修正 deleteValue/deleteValues 无法执行问题 異步 getValue/getValues/listValues 相关修改 Nov 16, 2025
@cyfung1031 cyfung1031 force-pushed the develop-values-api-8 branch 3 times, most recently from b6d77de to 31a4165 Compare November 17, 2025 23:52
@cyfung1031 cyfung1031 changed the title 異步 getValue/getValues/listValues 相关修改 [v1.3] 異步 getValue/getValues/listValues 相关修改 Nov 22, 2025
@CodFrm CodFrm requested a review from Copilot November 28, 2025 07:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

这个 PR 实现了异步 getValue/getValues/listValues 相关的重要改进,主要解决了在多标签页并发场景下值读取的一致性问题。通过引入 waitForFreshValueState 机制和批处理架构,确保在读取值之前能够获得最新的状态。

主要变更:

  • 新增 waitForFreshValueState 方法,确保在读取前获取最新的 value 状态
  • 重构 setValues 方法为批处理架构,使用任务队列和 setValuesByStorageName 进行批量处理
  • 修改 GM.getValue/GM.listValues/GM.getValues 以在读取前等待最新状态

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/app/service/service_worker/value.ts 核心变更:新增 waitForFreshValueStatesetValuesByStorageName,重构 value 更新逻辑为批处理模式,引入 ValueUpdateTaskInfo 任务队列
src/app/service/service_worker/value.test.ts 更新测试以反映新的批处理行为,调整期望值以匹配新的数据结构,添加 flush() 调用处理异步逻辑
src/app/service/service_worker/runtime.ts 更新 valueUpdate 订阅以使用新的 TScriptValueUpdate 类型,添加脚本状态重新验证逻辑
src/app/service/service_worker/permission_verify.ts 更新泛型约束从 TT extends Array<any> 以匹配 API 参数类型
src/app/service/service_worker/gm_api/gm_api.ts 新增 internalApiWaitForFreshValueState API 方法,修正权限链接配置
src/app/service/sandbox/runtime.ts 更新 valueUpdate 方法以处理新的 ValueUpdateSendData 数据结构
src/app/service/queue.ts 修改 TScriptValueUpdate 类型定义,从包含 Script 对象改为包含 uuidstatusisEarlyStart 字段
src/app/service/content/types.ts ValueUpdateDataEncoded 中添加 updatetime 字段,新增 ValueUpdateSendData 类型
src/app/service/content/script_executor.ts 更新 valueUpdate 方法签名以适配新的数据结构
src/app/service/content/inject.ts 更新类型引用从 ValueUpdateDataEncodedValueUpdateSendData
src/app/service/content/gm_api/gm_api.ts 实现 waitForFreshValueState 静态方法,更新 GM.getValue/GM.listValues/GM.getValues 以调用该方法,重构 valueUpdate 处理多个更新事件,引入 extValueStoreCopyreadFreshes 机制
src/app/service/content/gm_api/gm_api.test.ts 添加 valueDaoUpdatetimeFix 辅助函数,更新测试以处理新的 waitForFreshValueState 行为,修正正则表达式以匹配计数器格式
src/app/service/content/exec_script.ts 更新 valueUpdate 方法签名以传递 storageNameuuid 和数据列表

@CodFrm CodFrm added the P1 🔥 重要但是不紧急的内容 label Dec 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 🔥 重要但是不紧急的内容

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants