You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JS Interop从C#调用JS函数时出现跨浏览器函数未定义错误

Blazor JS Interop 调用window异步函数跨浏览器未定义问题解决

问题描述

我在Blazor中通过JS Interop从C#调用挂载在window对象上的异步JS函数,定义的JS函数如下:

第一个函数getAccount:

window.getAccount = async function () {
    try {
        if (provider === null) {
            return { "Success": false, "Address": null, "Message": "Metamask not detected" };
        }
        else {
            const accounts = await provider.request({ method: "eth_requestAccounts" });
            console.log("connected accounts = ", accounts);
            if (accounts.length !== 0 || accounts !== undefined) {
                return { "Success": true, "Message": null, "Address": accounts[0].toString() };
            }
            else
                return "new error";
        }
    } catch (err) {
            if (err.code === 4001) {
                return { "Success": false, "Address": null, "Message": "Allow to connect to Metamask" };
            } else {
                console.error(err);
                return { "Success": false, "Address": null, "Message": err.toString() };
            }
    }
}

第二个函数getChainId:

window.getChainId = async function () {
    return chainId.toString();
}

C#调用代码:

ConnectWallet connectResponse = await JsRuntime.InvokeAsync<ConnectWallet>("getAccount");
string chainId = await JsRuntime.InvokeAsync<string>("getChainId");

遇到的问题:Edge、Firefox中频繁出现**函数未定义(undefined)**错误,Chrome偶尔也会出现单个函数返回未定义的情况。

解决方案

1. 确保JS代码在Blazor初始化完成后挂载

Blazor框架初始化过程中,window对象或JS运行时可能未完全就绪,导致C#调用时函数还未挂载到window上。

  • 将自定义JS代码放在Blazor框架脚本之后,放在页面body末尾(WebAssembly项目修改wwwroot/index.html,Server项目修改Pages/_Host.cshtml):
<!-- Blazor框架脚本 -->
<script src="_framework/blazor.webassembly.js"></script>
<!-- 你的自定义JS代码 -->
<script>
// 这里放置window.getAccount和window.getChainId的定义
</script>
  • 组件中通过OnAfterRenderAsync生命周期方法,确保首次渲染完成后再调用JS函数:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        // 首次渲染完成后执行JS调用
        ConnectWallet connectResponse = await JsRuntime.InvokeAsync<ConnectWallet>("getAccount");
        string chainId = await JsRuntime.InvokeAsync<string>("getChainId");
    }
}

2. 修复全局变量依赖问题

getAccount和getChainId依赖全局变量provider和chainId,如果这些变量未初始化或获取失败,会导致函数逻辑异常,甚至间接引发"函数未定义"的假象。

  • 提前初始化全局变量并增加空值检查:
// 初始化provider和chainId
let provider = window.ethereum ?? null;
let chainId = null;

// 提前获取chainId
async function initChainId() {
    if (provider) {
        chainId = await provider.request({ method: 'eth_chainId' });
    }
}
initChainId();

window.getAccount = async function () {
    try {
        if (!provider) {
            return { "Success": false, "Address": null, "Message": "Metamask not detected" };
        }
        const accounts = await provider.request({ method: "eth_requestAccounts" });
        console.log("connected accounts = ", accounts);
        if (accounts?.length > 0) { // 使用可选链操作符避免undefined报错
            return { "Success": true, "Message": null, "Address": accounts[0].toString() };
        } else {
            return { "Success": false, "Address": null, "Message": "No accounts found" };
        }
    } catch (err) {
        if (err.code === 4001) {
            return { "Success": false, "Address": null, "Message": "Allow to connect to Metamask" };
        } else {
            return { "Success": false, "Address": null, "Message": err.toString() };
        }
    }
}

window.getChainId = async function () {
    if (!chainId && provider) {
        chainId = await provider.request({ method: 'eth_chainId' });
    }
    return (chainId ?? '0').toString();
}

3. 确保函数挂载的原子性

使用立即执行函数表达式(IIFE)包裹JS代码,确保函数同步挂载到window对象,避免异步执行导致的挂载延迟:

(function() {
    let provider = window.ethereum ?? null;
    let chainId = null;

    async function initChainId() {
        if (provider) {
            chainId = await provider.request({ method: 'eth_chainId' });
        }
    }
    initChainId();

    window.getAccount = async function () {
        // 原有逻辑...
    }

    window.getChainId = async function () {
        // 原有逻辑...
    }
})();

4. 统一返回值类型

getAccount函数中部分分支返回字符串"new error",但C#期望反序列化为ConnectWallet对象,会导致反序列化失败,间接表现为调用异常。确保所有分支返回结构一致的对象:

// 将返回"new error"的分支修改为:
return { "Success": false, "Address": null, "Message": "No accounts returned" };

5. 调试函数挂载状态

在浏览器控制台执行console.log(window.getAccount, window.getChainId),确认函数是否存在:

  • 如果函数不存在:检查JS代码的加载顺序和执行时机
  • 如果函数存在但调用报错:检查Blazor JS运行时的上下文绑定情况

内容的提问来源于stack exchange,提问作者Shubhankar Banerjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:19:54