Ember中incrementProperty是否为异步调用?测试中为何用run loop?
incrementProperty treated as an async call in Ember model tests? Great question! Let's break down why you need that run() wrapper even though your levelUp logic looks completely synchronous.
1. Ember's Run Loop: The Hidden Batch Processor
Ember uses a run loop to batch up and execute side effects like property change notifications, computed property recalculations, and DOM updates. In development mode, Ember automatically starts this run loop (called "autorun") to handle these tasks in the background. But in testing mode, autorun is disabled intentionally—this makes tests more predictable, ensuring that all side effects finish before your assertions run.
2. incrementProperty Triggers Reactive Side Effects
Even though incrementProperty looks like a simple synchronous value update, under the hood it calls Ember's set() method to modify the level property. This set() call triggers Ember's reactive system:
- It notifies any observers watching the
levelproperty - It marks any computed properties dependent on
levelas needing recalculation - These tasks get added to the run loop's queue waiting to be executed
In testing mode, since autorun is off, these queue tasks won't run unless you explicitly start the run loop with run().
3. Why Your Test Breaks Without run()
When you remove the run() wrapper and call player.levelUp() directly:
- The
levelvalue gets incremented immediately - But the reactive side effects (like any internal updates tied to the property change) stay stuck in the run loop queue
- Ember detects that you've triggered code with asynchronous side effects but haven't wrapped it in a run loop, so it throws that error to warn you.
Your test assertions would also likely fail even without the error, because the reactive updates (like any computed properties or state changes tied to level) wouldn't have finished processing when you check player.get('level') or player.get('levelName').
4. The Fix Makes Sense Now
Wrapping player.levelUp() in run(() => ...) manually starts the run loop, processes all queued side effects, and ensures everything is updated before your assertions run. That's why your test passes when you include it.
To sum up: It's not that incrementProperty is async itself, but that it triggers reactive side effects that rely on the run loop to execute—and testing mode forces you to handle that explicitly.
内容的提问来源于stack exchange,提问作者myartsev

