Mac Safari/Mac Outlook中JavaScript Async/Await参数异常求助
解决Safari中Office插件异步函数传参丢失及AES-CBC兼容性问题
核心症状
- Mac Outlook插件通过
displayDialogAsync加载的对话框空白,重构后Chrome/Edge/Firefox运行正常,但Safari中异步函数传入的任意参数均为undefined - 网页版
outlook.office365.com的Safari环境下,Office.context.mailbox.item.getBodyAsync()等所有Office API异步调用报错,将变量复制到全局空间后可正常运行 - Safari中AES-CBC加密生成的PIN值,与其他浏览器及后端PHP不兼容
- 表单提交时,输入框值传递到异步函数失败,临时方案为:通过构造函数/同步getter/setter预存参数,再调用无参数的
async/await函数,所有环境恢复正常
解决方案
1. 修复异步函数传参丢失问题
(1)规避嵌套异步闭包的作用域差异
Safari对嵌套异步函数的闭包处理存在特殊性,可将需要传递的参数提前绑定到全局/模块级作用域,避免在异步回调中直接传参:
let globalParentData; window.addEventListener("onload", () => { // 同步解析参数,脱离异步闭包 globalParentData = JSON.parse(window.atob(decodeURIComponent(o_q_s_data))); Office.onReady(async () => { await buildForm(null, globalParentData); }); });
(2)调整函数声明方式
赋值式声明的异步函数(buildForm = async (...) => {})在Safari中可能存在作用域提升问题,改为函数声明式:
async function buildForm(t, parentData) { const data = JSON.parse(parentData.data); // ...业务逻辑 }
(3)显式绑定上下文
用bind强制传递参数上下文,避免Safari的闭包参数丢失:
Office.onReady(async () => { const parentData = JSON.parse(window.atob(decodeURIComponent(o_q_s_data))); await buildForm.bind(null, null, parentData)(); });
2. 解决AES-CBC加密兼容性问题
(1)统一Web Crypto API的加密参数
Safari对AES-CBC的IV长度、密钥格式要求更严格,需确保:
- IV长度严格为16字节(AES块大小),不足时用PKCS#7填充
- 密钥导入时明确指定
extractable和keyUsages参数:
async function encryptWithAES(data, keyStr) { const encoder = new TextEncoder(); const iv = crypto.getRandomValues(new Uint8Array(16)); // 固定16字节IV // 标准化密钥导入 const cryptoKey = await crypto.subtle.importKey( "raw", encoder.encode(keyStr), { name: "AES-CBC" }, true, ["encrypt"] ); const encryptedBuffer = await crypto.subtle.encrypt( { name: "AES-CBC", iv: iv }, cryptoKey, encoder.encode(data) ); // 统一编码格式,与后端对齐 return { iv: btoa(String.fromCharCode(...iv)), encryptedData: btoa(String.fromCharCode(...new Uint8Array(encryptedBuffer))) }; }
(2)与后端PHP统一填充规则
PHP默认用零填充,而Web Crypto用PKCS#7填充,需两端统一:
- 前端加密前手动做PKCS#7填充
- 后端改用
openssl_encrypt并指定PKCS#7填充,示例:
$encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); // 前端解密时对应处理
3. 修复Office API异步调用报错问题
(1)确保Office API完全初始化
Safari中Office API的初始化可能存在延迟,需在Office.onReady中验证核心对象存在后再调用:
Office.onReady(async () => { const checkMailboxReady = () => { if (Office.context.mailbox?.item) { // 执行getBodyAsync等操作 Office.context.mailbox.item.getBodyAsync("html", (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { // 处理结果 } }); } else { setTimeout(checkMailboxReady, 100); // 重试 } }; checkMailboxReady(); });
(2)封装Office API为Promise
部分Office API不原生支持Promise,手动封装后可避免回调嵌套问题:
function getBodyAsyncPromise(format = "html") { return new Promise((resolve, reject) => { Office.context.mailbox.item.getBodyAsync(format, (result) => { result.status === Office.AsyncResultStatus.Succeeded ? resolve(result.value) : reject(result.error); }); }); } // 调用示例 Office.onReady(async () => { const body = await getBodyAsyncPromise(); // ...后续逻辑 });
内容的提问来源于stack exchange,提问作者David E. Craig
相关产品推荐
相关产品推荐

