MobX.js 提供了一种响应式编程的方式,可以自动跟踪状态的变化并对此做出反应。在 MobX 中,when
函数是一种便利的机制,用于观察一个特定的条件,并在该条件成立时执行一些操作。
最简单的例子:
async function fetchData() {
await when(() => that.isVisible)
// do your staff
}
when
函数接收两个参数,第一个参数是一个返回布尔值的函数,第二个参数是一个回调函数。当第一个函数返回 true
时,MobX 将运行第二个参数的回调函数,并且自动清理所有的相关依赖。
此外,when
还返回一个可用于手动清理依赖的函数,如果你想在条件尚未满足时就中止观察,可以调用这个函数。
对于异步操作,when
函数还有一个更有用的特性,你可以在 when
函数前加上 await
关键字,从而等待条件成立。这可以让你用同步的方式编写异步的代码。
MobX 需要 when
函数,因为这是观察状态并在条件满足时执行操作的一种强大方式。使用 when
,你可以编写出更清晰、更直观的代码,也可以避免不必要的状态检查和循环等待。
需求背景是获取到远端的 canUseMembers 后才进行初始化,但是 remoteStore 可能是被业务初始化好,也可能没有初始化好,或者可能被清理了数据,或者说前置有调用 getRemoteConfigs 的逻辑。那么此时,我们需要使用 watch 来保证远端数据是取好的,然后开始初始化。
<script setup lang="ts">
import { RemoteStore } from './RemoteStore';
const remoteStore = RemoteStore();
const useRemoteData = () => {
const remoteMembers = ref([]);
const init = async () => {
try {
const res = await fetchRemoteMembers();
remoteMembers.value = res;
} catch(err){
console.error(err);
}
}
watch(() => remoteStore.remoteConfigs.value.canUseMembers.length > 0, () => {
init();
})
}
</script>
RemoteStore 模块的结合实现👇🏻
import { watch, ref } from 'vue';
const RemoteStore = () => {
const remoteConfigs = ref({});
// 不确定什么时候调用 getRemoteConfigs
const getRemoteConfigs = async () => {
const result = await fetchRemoteConfigs();
remoteConfigs.value = result;
}
return {
remoteConfigs
}
}
when 函数的实现主要运用了 Vue.js 的 watch 和 onScopeDispose。我们在给定的作用t域内运行一个新的效果,当这个效果返回 true 时,我们解析 Promise。同时我们还添加了一个清理函数,当作用域被销毁时,我们清理定时器并拒绝 Promise。
这里用到了 Vue 的 EffectScope,请查阅 进阶:搞懂 Vue 的 EffectScope
import { type EffectScope, watch, onScopeDispose } from 'vue';
/**
* @see https://mobx.js.org/reactions.html#await-when
*/
export function when(
effect: () => boolean,
scope: EffectScope,
options?: {
/** 超时时间,超出时间后调用 Promise.reject */
timeout?: number;
},
): Promise<void> {
return new Promise<void>((resolve, reject) => {
let timeoutTimer = 0;
if (options && options.timeout) {
timeoutTimer = window.setTimeout(() => {
reject(new Error('Effect function timeout'));
}, options.timeout);
}
scope.run(() => {
let disposed = false;
watch(
effect,
flag => {
if (flag && !disposed) {
disposed = true;
resolve();
}
},
{ immediate: true },
);
onScopeDispose(() => {
if (timeoutTimer) {
window.clearTimeout(timeoutTimer);
}
reject(new Error('Scope disposed'));
});
});
});
}
when 函数接收三个参数:
effect: 一个返回布尔值的函数,当这个函数返回 true 时,when 返回的 Promise 会被解析。
scope: 一个 EffectScope 对象,when 中的效果会在这个作用域内运行。
options: 包含选项的对象。当前只支持 timeout 属性,表示在超时后应拒绝 Promise。
现在,你可以在vue组件中、hook函数中通过when来收敛你的逻辑代码了。