Office JS Outlook插件RoamingSettings接口异常问题排查
Outlook插件RoamingSettings跨上下文访问异常问题分析
问题场景
开发Office JS Outlook插件时,发现RoamingSettings存在异常行为。插件包含两个按钮,manifest.xml中的声明如下:
<OfficeTab id="TabDefault"> <Group id="apptComposeGroup"> <Control xsi:type="Button" id="loginButton"> ... <Action xsi:type="ExecuteFunction"> <FunctionName>login</FunctionName> </Action> <Control> <Control xsi:type="Button" id="logoutButton"> ... <Action xsi:type="ExecuteFunction"> <FunctionName>logout</FunctionName> </Action> <Control> </Group> </OfficeTab>
commands.ts中的代码:
async function login(event){ console.log('1', Office.context.roamingSettings.get('foo')); Office.context.roamingSettings.set('foo', 'bar') Office.context.roamingSettings.saveAsync(); console.log('2', Office.context.roamingSettings.get('foo')); event.completed({ allowEvent: true }); } async function logout(event){ console.log('3', Office.context.roamingSettings.get('foo')); Office.context.roamingSettings.remove('foo') Office.context.roamingSettings.saveAsync(); console.log('4', Office.context.roamingSettings.get('foo')); event.completed({ allowEvent: true }); } Office.actions.associate(login.name, login); Office.actions.associate(logout.name, logout);
按「登录→登出→登录」顺序点击按钮,控制台输出:
1 undefined 2 bar 3 undefined 4 undefined 1 bar 2 bar
按设计RoamingSettings应存储在邮箱级别,可跨上下文访问同一设置,但实际不同方法中的设置似乎相互独立。
异常原因
核心问题在于**saveAsync是异步操作**,调用saveAsync()后立刻执行event.completed()结束上下文,导致设置还没真正同步到服务器,不同命令上下文(每个按钮点击都是独立的命令执行上下文)读取的是本地缓存的旧值:
- 第一次登录:修改内存中的
foo后,saveAsync未完成就结束上下文,服务器可能还没更新foo值 - 登出操作:新上下文读取的是未同步的本地缓存,所以
foo为undefined,移除操作同样未完成保存就结束上下文 - 第二次登录:此时服务器可能残留第一次未完成保存的
foo='bar',新上下文读取到这个值,导致出现异常
解决方法
1. 等待saveAsync完成后再结束上下文
必须确保saveAsync执行完成(成功或失败)后,再调用event.completed(),保证设置同步到服务器:
async function login(event){ console.log('1', Office.context.roamingSettings.get('foo')); Office.context.roamingSettings.set('foo', 'bar'); Office.context.roamingSettings.saveAsync((asyncResult) => { if (asyncResult.status === Office.AsyncResultStatus.Succeeded) { console.log('2', Office.context.roamingSettings.get('foo')); } else { console.error('保存设置失败', asyncResult.error); } event.completed({ allowEvent: true }); }); } async function logout(event){ console.log('3', Office.context.roamingSettings.get('foo')); Office.context.roamingSettings.remove('foo'); Office.context.roamingSettings.saveAsync((asyncResult) => { if (asyncResult.status === Office.AsyncResultStatus.Succeeded) { console.log('4', Office.context.roamingSettings.get('foo')); } else { console.error('移除设置失败', asyncResult.error); } event.completed({ allowEvent: true }); }); }
2. 进入上下文时刷新缓存
每个命令上下文是独立的,建议在函数开头调用refreshAsync(),确保读取到服务器最新的设置:
async function login(event){ Office.context.roamingSettings.refreshAsync((refreshResult) => { if (refreshResult.status === Office.AsyncResultStatus.Succeeded) { console.log('1', Office.context.roamingSettings.get('foo')); Office.context.roamingSettings.set('foo', 'bar'); Office.context.roamingSettings.saveAsync((saveResult) => { // 处理保存结果 event.completed({ allowEvent: true }); }); } else { console.error('刷新缓存失败', refreshResult.error); event.completed({ allowEvent: true }); } }); }
内容的提问来源于stack exchange,提问作者Dumitru Bereghici
相关产品推荐
相关产品推荐

