When条件执行 #

when 是 MobX 提供的一个条件响应函数。它会观察条件,当条件变为 true 时执行副作用,然后自动清除。when 非常适合处理"等待某个状态"的场景。

基本用法 #

简单示例 #

javascript
import { makeAutoObservable, when } from 'mobx';

class Store {
  isLoggedIn = false;
  user = null;

  constructor() {
    makeAutoObservable(this);
  }
}

const store = new Store();

// 当 isLoggedIn 变为 true 时执行
when(
  () => store.isLoggedIn,
  () => {
    console.log('User is now logged in!');
    loadUserData();
  }
);

// 触发条件
store.isLoggedIn = true;  // 输出: User is now logged in!
// when 自动清除,之后的变化不再触发
store.isLoggedIn = false;
store.isLoggedIn = true;  // 不会再次触发

工作原理 #

text
┌─────────────────────────────────────────────────────────────┐
│                     when 工作流程                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   when(                                                     │
│     () => condition,    // 1. 条件函数                      │
│     () => { }           // 2. 副作用函数                    │
│   )                                                         │
│                                                             │
│   执行流程:                                                 │
│   1. 检查条件是否为 true                                     │
│      ├── true:立即执行副作用,然后清除                      │
│      └── false:等待条件变化                                │
│   2. 条件变为 true 时,执行副作用                            │
│   3. 自动清除,不再监听                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

返回Promise #

when 可以不传副作用函数,返回一个 Promise:

javascript
import { when } from 'mobx';

// 返回 Promise
await when(() => store.isLoggedIn);
console.log('User is logged in!');

// 在 async 函数中使用
async function waitForLogin() {
  await when(() => store.isLoggedIn);
  console.log('User logged in, loading data...');
  await loadUserData();
}

清除when #

when 返回一个清除函数:

javascript
const dispose = when(
  () => store.condition,
  () => {
    console.log('Condition met!');
  }
);

// 手动清除
dispose();

使用场景 #

等待数据加载完成 #

javascript
import { makeAutoObservable, when } from 'mobx';

class DataStore {
  data = null;
  loading = false;

  constructor() {
    makeAutoObservable(this);
  }

  async fetchData() {
    this.loading = true;
    const response = await fetch('/api/data');
    this.data = await response.json();
    this.loading = false;
  }
}

const store = new DataStore();
store.fetchData();

// 等待数据加载完成
when(
  () => store.data !== null,
  () => {
    console.log('Data loaded:', store.data);
    processData(store.data);
  }
);

等待用户登录 #

javascript
import { makeAutoObservable, when } from 'mobx';

class AuthStore {
  isLoggedIn = false;
  user = null;

  constructor() {
    makeAutoObservable(this);
  }

  async login(credentials) {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    });
    const user = await response.json();
    this.user = user;
    this.isLoggedIn = true;
  }
}

const authStore = new AuthStore();

// 等待登录后执行操作
async function performActionAfterLogin() {
  await when(() => authStore.isLoggedIn);
  console.log('User logged in:', authStore.user.name);
  await loadPrivateData();
}

等待条件满足 #

javascript
import { makeAutoObservable, when } from 'mobx';

class GameStore {
  score = 0;
  level = 1;
  gameOver = false;

  constructor() {
    makeAutoObservable(this);

    // 等待达到特定分数
    when(
      () => this.score >= 1000,
      () => {
        console.log('Achievement unlocked: Score 1000!');
        this.unlockAchievement('score_1000');
      }
    );

    // 等待游戏结束
    when(
      () => this.gameOver,
      () => {
        this.showGameOverScreen();
      }
    );
  }

  addScore(points) {
    this.score += points;
  }

  endGame() {
    this.gameOver = true;
  }

  unlockAchievement(id) { }
  showGameOverScreen() { }
}

完整示例 #

模态框等待 #

javascript
import { makeAutoObservable, when } from 'mobx';

class ModalStore {
  isOpen = false;
  result = null;

  constructor() {
    makeAutoObservable(this);
  }

  open() {
    this.isOpen = true;
    this.result = null;
  }

  close(result = null) {
    this.isOpen = false;
    this.result = result;
  }
}

const confirmStore = new ModalStore();

// 显示确认对话框并等待结果
async function showConfirm(message) {
  confirmStore.open();

  // 等待对话框关闭
  await when(() => !confirmStore.isOpen);

  return confirmStore.result;
}

// 使用
async function deleteItem() {
  const confirmed = await showConfirm('Are you sure?');
  if (confirmed) {
    // 执行删除
  }
}

表单验证等待 #

javascript
import { makeAutoObservable, when } from 'mobx';

class FormStore {
  values = {};
  errors = {};
  validating = false;

  constructor() {
    makeAutoObservable(this);
  }

  get isValid() {
    return Object.keys(this.errors).length === 0;
  }

  async validate() {
    this.validating = true;

    // 模拟异步验证
    await new Promise(resolve => setTimeout(resolve, 500));

    // 验证逻辑
    this.errors = {};
    if (!this.values.email) {
      this.errors.email = 'Email is required';
    }

    this.validating = false;
  }

  async submit() {
    await this.validate();

    // 等待验证完成且有效
    await when(() => !this.validating);

    if (this.isValid) {
      await this.sendForm();
    }
  }

  async sendForm() {
    // 发送表单
  }
}

文件上传等待 #

javascript
import { makeAutoObservable, when, runInAction } from 'mobx';

class UploadStore {
  files = [];
  uploading = false;
  progress = 0;

  constructor() {
    makeAutoObservable(this);
  }

  async uploadFiles(files) {
    this.uploading = true;
    this.progress = 0;

    for (let i = 0; i < files.length; i++) {
      await this.uploadFile(files[i]);
      runInAction(() => {
        this.progress = ((i + 1) / files.length) * 100;
      });
    }

    runInAction(() => {
      this.uploading = false;
    });
  }

  async uploadFile(file) {
    // 上传单个文件
  }
}

const uploadStore = new UploadStore();

// 等待上传完成
async function waitForUpload() {
  uploadStore.uploadFiles(selectedFiles);

  await when(() => !uploadStore.uploading);
  console.log('All files uploaded!');
}

WebSocket连接等待 #

javascript
import { makeAutoObservable, when, runInAction } from 'mobx';

class WebSocketStore {
  connected = false;
  socket = null;

  constructor() {
    makeAutoObservable(this);
    this.connect();
  }

  connect() {
    this.socket = new WebSocket('ws://localhost:8080');

    this.socket.onopen = () => {
      runInAction(() => {
        this.connected = true;
      });
    };

    this.socket.onclose = () => {
      runInAction(() => {
        this.connected = false;
      });
    };
  }

  async send(message) {
    // 等待连接建立
    await when(() => this.connected);
    this.socket.send(JSON.stringify(message));
  }

  disconnect() {
    if (this.socket) {
      this.socket.close();
    }
  }
}

const wsStore = new WebSocketStore();

// 等待连接后发送消息
async function sendMessage(data) {
  await wsStore.send({ type: 'message', data });
}

高级用法 #

超时处理 #

javascript
async function waitForConditionWithTimeout(timeout = 5000) {
  const result = await Promise.race([
    when(() => store.condition),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ]);
  return result;
}

// 使用
try {
  await waitForConditionWithTimeout();
  console.log('Condition met!');
} catch (error) {
  console.log('Timeout waiting for condition');
}

条件组合 #

javascript
// 等待多个条件
when(
  () => store.isLoggedIn && store.dataLoaded,
  () => {
    console.log('Ready to show dashboard');
  }
);

// 等待任一条件
when(
  () => store.success || store.error,
  () => {
    if (store.success) {
      showSuccess();
    } else {
      showError(store.error);
    }
  }
);

嵌套when #

javascript
when(
  () => store.step === 1,
  () => {
    console.log('Step 1 complete');
    when(
      () => store.step === 2,
      () => {
        console.log('Step 2 complete');
      }
    );
  }
);

when vs reaction vs autorun #

特性 when reaction autorun
执行次数 一次 多次 多次
条件触发
立即执行 条件为true时 否(默认)
自动清除
返回Promise 支持

选择建议:

  • 等待某个条件满足:使用 when
  • 条件满足后只执行一次:使用 when
  • 条件满足后多次执行:使用 reaction
  • 立即执行并持续响应:使用 autorun

总结 #

when 的核心要点:

  • 一次性执行:条件满足后自动清除
  • 条件驱动:等待条件变为 true
  • Promise支持:可以返回 Promise
  • 适合等待场景:非常适合"等待某事发生"的场景

使用场景:

  • 等待数据加载完成
  • 等待用户登录
  • 等待条件满足
  • 等待异步操作完成

继续学习 Observer观察者,了解 MobX 与 React 的集成。

最后更新:2026-03-28