xstate 4.32.1 → 4.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/es/interpreter.js CHANGED
@@ -1,13 +1,12 @@
1
1
  import { __values, __spreadArray, __read, __assign } from './_virtual/_tslib.js';
2
- import { ActionTypes, SpecialTargets } from './types.js';
2
+ import { SpecialTargets, ActionTypes } from './types.js';
3
3
  import { isStateConfig, State, bindActionToState } from './State.js';
4
- import { errorPlatform, log, stop, start, cancel, send, update, error as error$1 } from './actionTypes.js';
5
- import { doneInvoke, initEvent, getActionFunction, error } from './actions.js';
4
+ import { raise, send, errorPlatform, update, error as error$1, log, stop, start, cancel } from './actionTypes.js';
5
+ import { initEvent, doneInvoke, toActionObjects, resolveActions, error, getActionFunction } from './actions.js';
6
6
  import { IS_PRODUCTION } from './environment.js';
7
- import { warn, mapContext, isFunction, toSCXMLEvent, toInvokeSource, isMachine, isPromiseLike, isObservable, isBehavior, reportUnhandledExceptionOnInvocation, symbolObservable, isArray, toEventObject, isString, isActor, uniqueId, toObserver } from './utils.js';
7
+ import { warn, mapContext, toObserver, isFunction, toSCXMLEvent, flatten, isPromiseLike, isObservable, isMachine, isBehavior, reportUnhandledExceptionOnInvocation, symbolObservable, isArray, toEventObject, isString, isActor, toInvokeSource, uniqueId } from './utils.js';
8
8
  import { Scheduler } from './scheduler.js';
9
- import { isSpawnedActor, createDeferredActor } from './Actor.js';
10
- import { isInFinalState } from './stateUtils.js';
9
+ import { createDeferredActor, isSpawnedActor } from './Actor.js';
11
10
  import { registry } from './registry.js';
12
11
  import { getGlobal, registerService } from './devTools.js';
13
12
  import { provide, consume } from './serviceScope.js';
@@ -101,7 +100,7 @@ function () {
101
100
  // Forward copy of event to child actors
102
101
  _this.forward(_event);
103
102
 
104
- var nextState = _this.nextState(_event);
103
+ var nextState = _this._nextState(_event);
105
104
 
106
105
  _this.update(nextState, _event);
107
106
  });
@@ -128,17 +127,176 @@ function () {
128
127
  }
129
128
 
130
129
  if ('machine' in target) {
131
- // Send SCXML events to machines
132
- target.send(__assign(__assign({}, event), {
133
- name: event.name === error$1 ? "".concat(error(_this.id)) : event.name,
134
- origin: _this.sessionId
135
- }));
130
+ // perhaps those events should be rejected in the parent
131
+ // but atm it doesn't have easy access to all of the information that is required to do it reliably
132
+ if (_this.status !== InterpreterStatus.Stopped || _this.parent !== target || // we need to send events to the parent from exit handlers of a machine that reached its final state
133
+ _this.state.done) {
134
+ // Send SCXML events to machines
135
+ target.send(__assign(__assign({}, event), {
136
+ name: event.name === error$1 ? "".concat(error(_this.id)) : event.name,
137
+ origin: _this.sessionId
138
+ }));
139
+ }
136
140
  } else {
137
141
  // Send normal events to other targets
138
142
  target.send(event.data);
139
143
  }
140
144
  };
141
145
 
146
+ this._exec = function (action, context, _event, actionFunctionMap) {
147
+ if (actionFunctionMap === void 0) {
148
+ actionFunctionMap = _this.machine.options.actions;
149
+ }
150
+
151
+ var actionOrExec = action.exec || getActionFunction(action.type, actionFunctionMap);
152
+ var exec = isFunction(actionOrExec) ? actionOrExec : actionOrExec ? actionOrExec.exec : action.exec;
153
+
154
+ if (exec) {
155
+ try {
156
+ return exec(context, _event.data, !_this.machine.config.predictableActionArguments ? {
157
+ action: action,
158
+ state: _this.state,
159
+ _event: _event
160
+ } : {
161
+ action: action,
162
+ _event: _event
163
+ });
164
+ } catch (err) {
165
+ if (_this.parent) {
166
+ _this.parent.send({
167
+ type: 'xstate.error',
168
+ data: err
169
+ });
170
+ }
171
+
172
+ throw err;
173
+ }
174
+ }
175
+
176
+ switch (action.type) {
177
+ case send:
178
+ var sendAction = action;
179
+
180
+ if (typeof sendAction.delay === 'number') {
181
+ _this.defer(sendAction);
182
+
183
+ return;
184
+ } else {
185
+ if (sendAction.to) {
186
+ _this.sendTo(sendAction._event, sendAction.to);
187
+ } else {
188
+ _this.send(sendAction._event);
189
+ }
190
+ }
191
+
192
+ break;
193
+
194
+ case cancel:
195
+ _this.cancel(action.sendId);
196
+
197
+ break;
198
+
199
+ case start:
200
+ {
201
+ if (_this.status !== InterpreterStatus.Running) {
202
+ return;
203
+ }
204
+
205
+ var activity = action.activity; // If the activity will be stopped right after it's started
206
+ // (such as in transient states)
207
+ // don't bother starting the activity.
208
+
209
+ if ( // in v4 with `predictableActionArguments` invokes are called eagerly when the `this.state` still points to the previous state
210
+ !_this.machine.config.predictableActionArguments && !_this.state.activities[activity.id || activity.type]) {
211
+ break;
212
+ } // Invoked services
213
+
214
+
215
+ if (activity.type === ActionTypes.Invoke) {
216
+ var invokeSource = toInvokeSource(activity.src);
217
+ var serviceCreator = _this.machine.options.services ? _this.machine.options.services[invokeSource.type] : undefined;
218
+ var id = activity.id,
219
+ data = activity.data;
220
+
221
+ if (!IS_PRODUCTION) {
222
+ warn(!('forward' in activity), // tslint:disable-next-line:max-line-length
223
+ "`forward` property is deprecated (found in invocation of '".concat(activity.src, "' in in machine '").concat(_this.machine.id, "'). ") + "Please use `autoForward` instead.");
224
+ }
225
+
226
+ var autoForward = 'autoForward' in activity ? activity.autoForward : !!activity.forward;
227
+
228
+ if (!serviceCreator) {
229
+ // tslint:disable-next-line:no-console
230
+ if (!IS_PRODUCTION) {
231
+ warn(false, "No service found for invocation '".concat(activity.src, "' in machine '").concat(_this.machine.id, "'."));
232
+ }
233
+
234
+ return;
235
+ }
236
+
237
+ var resolvedData = data ? mapContext(data, context, _event) : undefined;
238
+
239
+ if (typeof serviceCreator === 'string') {
240
+ // TODO: warn
241
+ return;
242
+ }
243
+
244
+ var source = isFunction(serviceCreator) ? serviceCreator(context, _event.data, {
245
+ data: resolvedData,
246
+ src: invokeSource,
247
+ meta: activity.meta
248
+ }) : serviceCreator;
249
+
250
+ if (!source) {
251
+ // TODO: warn?
252
+ return;
253
+ }
254
+
255
+ var options = void 0;
256
+
257
+ if (isMachine(source)) {
258
+ source = resolvedData ? source.withContext(resolvedData) : source;
259
+ options = {
260
+ autoForward: autoForward
261
+ };
262
+ }
263
+
264
+ _this.spawn(source, id, options);
265
+ } else {
266
+ _this.spawnActivity(activity);
267
+ }
268
+
269
+ break;
270
+ }
271
+
272
+ case stop:
273
+ {
274
+ _this.stopChild(action.activity.id);
275
+
276
+ break;
277
+ }
278
+
279
+ case log:
280
+ var label = action.label,
281
+ value = action.value;
282
+
283
+ if (label) {
284
+ _this.logger(label, value);
285
+ } else {
286
+ _this.logger(value);
287
+ }
288
+
289
+ break;
290
+
291
+ default:
292
+ if (!IS_PRODUCTION) {
293
+ warn(false, "No implementation found for action type '".concat(action.type, "'"));
294
+ }
295
+
296
+ break;
297
+ }
298
+ };
299
+
142
300
  var resolvedOptions = __assign(__assign({}, Interpreter.defaultOptions), options);
143
301
 
144
302
  var clock = resolvedOptions.clock,
@@ -222,7 +380,9 @@ function () {
222
380
 
223
381
  this._state = state; // Execute actions
224
382
 
225
- if (this.options.execute) {
383
+ if ((!this.machine.config.predictableActionArguments || // this is currently required to execute initial actions as the `initialState` gets cached
384
+ // we can't just recompute it (and execute actions while doing so) because we try to preserve identity of actors created within initial assigns
385
+ _event === initEvent) && this.options.execute) {
226
386
  this.execute(this.state);
227
387
  } // Update children
228
388
 
@@ -289,9 +449,7 @@ function () {
289
449
  }
290
450
  }
291
451
 
292
- var isDone = isInFinalState(state.configuration || [], this.machine);
293
-
294
- if (this.state.configuration && isDone) {
452
+ if (this.state.done) {
295
453
  // get final child state node
296
454
  var finalChildStateNode = state.configuration.find(function (sn) {
297
455
  return sn.type === 'final' && sn.parent === _this.machine;
@@ -315,7 +473,9 @@ function () {
315
473
  }
316
474
  }
317
475
 
318
- this.stop();
476
+ this._stop();
477
+
478
+ this._stopChildren();
319
479
  }
320
480
  };
321
481
  /*
@@ -340,42 +500,35 @@ function () {
340
500
  completeListener) {
341
501
  var _this = this;
342
502
 
343
- if (!nextListenerOrObserver) {
344
- return {
345
- unsubscribe: function () {
346
- return void 0;
347
- }
348
- };
349
- }
350
-
351
- var listener;
352
- var resolvedCompleteListener = completeListener;
503
+ var observer = toObserver(nextListenerOrObserver, _, completeListener);
504
+ this.listeners.add(observer.next); // Send current state to listener
353
505
 
354
- if (typeof nextListenerOrObserver === 'function') {
355
- listener = nextListenerOrObserver;
356
- } else {
357
- listener = nextListenerOrObserver.next.bind(nextListenerOrObserver);
358
- resolvedCompleteListener = nextListenerOrObserver.complete.bind(nextListenerOrObserver);
506
+ if (this.status !== InterpreterStatus.NotStarted) {
507
+ observer.next(this.state);
359
508
  }
360
509
 
361
- this.listeners.add(listener); // Send current state to listener
510
+ var completeOnce = function () {
511
+ _this.doneListeners.delete(completeOnce);
362
512
 
363
- if (this.status !== InterpreterStatus.NotStarted) {
364
- listener(this.state);
365
- }
513
+ _this.stopListeners.delete(completeOnce);
366
514
 
367
- if (resolvedCompleteListener) {
368
- if (this.status === InterpreterStatus.Stopped) {
369
- resolvedCompleteListener();
370
- } else {
371
- this.onDone(resolvedCompleteListener);
372
- }
515
+ observer.complete();
516
+ };
517
+
518
+ if (this.status === InterpreterStatus.Stopped) {
519
+ observer.complete();
520
+ } else {
521
+ this.onDone(completeOnce);
522
+ this.onStop(completeOnce);
373
523
  }
374
524
 
375
525
  return {
376
526
  unsubscribe: function () {
377
- listener && _this.listeners.delete(listener);
378
- resolvedCompleteListener && _this.doneListeners.delete(resolvedCompleteListener);
527
+ _this.listeners.delete(observer.next);
528
+
529
+ _this.doneListeners.delete(completeOnce);
530
+
531
+ _this.stopListeners.delete(completeOnce);
379
532
  }
380
533
  };
381
534
  };
@@ -480,18 +633,20 @@ function () {
480
633
  });
481
634
  return this;
482
635
  };
483
- /**
484
- * Stops the interpreter and unsubscribe all listeners.
485
- *
486
- * This will also notify the `onStop` listeners.
487
- */
488
636
 
637
+ Interpreter.prototype._stopChildren = function () {
638
+ // TODO: think about converting those to actions
639
+ this.children.forEach(function (child) {
640
+ if (isFunction(child.stop)) {
641
+ child.stop();
642
+ }
643
+ });
644
+ this.children.clear();
645
+ };
489
646
 
490
- Interpreter.prototype.stop = function () {
647
+ Interpreter.prototype._stop = function () {
491
648
  var e_6, _a, e_7, _b, e_8, _c, e_9, _d, e_10, _e;
492
649
 
493
- var _this = this;
494
-
495
650
  try {
496
651
  for (var _f = __values(this.listeners), _g = _f.next(); !_g.done; _g = _f.next()) {
497
652
  var listener = _g.value;
@@ -567,40 +722,13 @@ function () {
567
722
  return this;
568
723
  }
569
724
 
570
- __spreadArray([], __read(this.state.configuration), false).sort(function (a, b) {
571
- return b.order - a.order;
572
- }).forEach(function (stateNode) {
573
- var e_11, _a;
574
-
575
- try {
576
- for (var _b = __values(stateNode.definition.exit), _c = _b.next(); !_c.done; _c = _b.next()) {
577
- var action = _c.value;
578
-
579
- _this.exec(action, _this.state);
580
- }
581
- } catch (e_11_1) {
582
- e_11 = {
583
- error: e_11_1
584
- };
585
- } finally {
586
- try {
587
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
588
- } finally {
589
- if (e_11) throw e_11.error;
590
- }
591
- }
592
- }); // Stop all children
593
-
594
-
595
- this.children.forEach(function (child) {
596
- if (isFunction(child.stop)) {
597
- child.stop();
598
- }
599
- });
600
- this.children.clear();
725
+ this.initialized = false;
726
+ this.status = InterpreterStatus.Stopped;
727
+ this._initialState = undefined;
601
728
 
602
729
  try {
603
- // Cancel all delayed events
730
+ // we are going to stop within the current sync frame
731
+ // so we can safely just cancel this here as nothing async should be fired anyway
604
732
  for (var _p = __values(Object.keys(this.delayedEventsMap)), _q = _p.next(); !_q.done; _q = _p.next()) {
605
733
  var key = _q.value;
606
734
  this.clock.clearTimeout(this.delayedEventsMap[key]);
@@ -615,16 +743,77 @@ function () {
615
743
  } finally {
616
744
  if (e_10) throw e_10.error;
617
745
  }
618
- }
746
+ } // clear everything that might be enqueued
747
+
619
748
 
620
749
  this.scheduler.clear();
621
750
  this.scheduler = new Scheduler({
622
751
  deferEvents: this.options.deferEvents
623
752
  });
624
- this.initialized = false;
625
- this.status = InterpreterStatus.Stopped;
626
- this._initialState = undefined;
627
- registry.free(this.sessionId);
753
+ };
754
+ /**
755
+ * Stops the interpreter and unsubscribe all listeners.
756
+ *
757
+ * This will also notify the `onStop` listeners.
758
+ */
759
+
760
+
761
+ Interpreter.prototype.stop = function () {
762
+ // TODO: add warning for stopping non-root interpreters
763
+ var _this = this; // grab the current scheduler as it will be replaced in _stop
764
+
765
+
766
+ var scheduler = this.scheduler;
767
+
768
+ this._stop(); // let what is currently processed to be finished
769
+
770
+
771
+ scheduler.schedule(function () {
772
+ // it feels weird to handle this here but we need to handle this even slightly "out of band"
773
+ var _event = toSCXMLEvent({
774
+ type: 'xstate.stop'
775
+ });
776
+
777
+ var nextState = provide(_this, function () {
778
+ var exitActions = flatten(__spreadArray([], __read(_this.state.configuration), false).sort(function (a, b) {
779
+ return b.order - a.order;
780
+ }).map(function (stateNode) {
781
+ return toActionObjects(stateNode.onExit, _this.machine.options.actions);
782
+ }));
783
+
784
+ var _a = __read(resolveActions(_this.machine, _this.state, _this.state.context, _event, exitActions, _this.machine.config.predictableActionArguments ? _this._exec : undefined, _this.machine.config.predictableActionArguments || _this.machine.config.preserveActionOrder), 2),
785
+ resolvedActions = _a[0],
786
+ updatedContext = _a[1];
787
+
788
+ var newState = new State({
789
+ value: _this.state.value,
790
+ context: updatedContext,
791
+ _event: _event,
792
+ _sessionid: _this.sessionId,
793
+ historyValue: undefined,
794
+ history: _this.state,
795
+ actions: resolvedActions.filter(function (action) {
796
+ return action.type !== raise && (action.type !== send || !!action.to && action.to !== SpecialTargets.Internal);
797
+ }),
798
+ activities: {},
799
+ events: [],
800
+ configuration: [],
801
+ transitions: [],
802
+ children: {},
803
+ done: _this.state.done,
804
+ tags: _this.state.tags,
805
+ machine: _this.machine
806
+ });
807
+ newState.changed = true;
808
+ return newState;
809
+ });
810
+
811
+ _this.update(nextState, _event);
812
+
813
+ _this._stopChildren();
814
+
815
+ registry.free(_this.sessionId);
816
+ });
628
817
  return this;
629
818
  };
630
819
 
@@ -642,7 +831,7 @@ function () {
642
831
  }
643
832
 
644
833
  this.scheduler.schedule(function () {
645
- var e_12, _a;
834
+ var e_11, _a;
646
835
 
647
836
  var nextState = _this.state;
648
837
  var batchChanged = false;
@@ -668,15 +857,15 @@ function () {
668
857
 
669
858
  _loop_1(event_1);
670
859
  }
671
- } catch (e_12_1) {
672
- e_12 = {
673
- error: e_12_1
860
+ } catch (e_11_1) {
861
+ e_11 = {
862
+ error: e_11_1
674
863
  };
675
864
  } finally {
676
865
  try {
677
866
  if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
678
867
  } finally {
679
- if (e_12) throw e_12.error;
868
+ if (e_11) throw e_11.error;
680
869
  }
681
870
  }
682
871
 
@@ -696,18 +885,14 @@ function () {
696
885
  Interpreter.prototype.sender = function (event) {
697
886
  return this.send.bind(this, event);
698
887
  };
699
- /**
700
- * Returns the next state given the interpreter's current state and the event.
701
- *
702
- * This is a pure method that does _not_ update the interpreter's state.
703
- *
704
- * @param event The event to determine the next state
705
- */
706
888
 
707
-
708
- Interpreter.prototype.nextState = function (event) {
889
+ Interpreter.prototype._nextState = function (event, exec) {
709
890
  var _this = this;
710
891
 
892
+ if (exec === void 0) {
893
+ exec = !!this.machine.config.predictableActionArguments && this._exec;
894
+ }
895
+
711
896
  var _event = toSCXMLEvent(event);
712
897
 
713
898
  if (_event.name.indexOf(errorPlatform) === 0 && !this.state.nextEvents.some(function (nextEvent) {
@@ -717,13 +902,25 @@ function () {
717
902
  }
718
903
 
719
904
  var nextState = provide(this, function () {
720
- return _this.machine.transition(_this.state, _event);
905
+ return _this.machine.transition(_this.state, _event, undefined, exec || undefined);
721
906
  });
722
907
  return nextState;
723
908
  };
909
+ /**
910
+ * Returns the next state given the interpreter's current state and the event.
911
+ *
912
+ * This is a pure method that does _not_ update the interpreter's state.
913
+ *
914
+ * @param event The event to determine the next state
915
+ */
916
+
917
+
918
+ Interpreter.prototype.nextState = function (event) {
919
+ return this._nextState(event, false);
920
+ };
724
921
 
725
922
  Interpreter.prototype.forward = function (event) {
726
- var e_13, _a;
923
+ var e_12, _a;
727
924
 
728
925
  try {
729
926
  for (var _b = __values(this.forwardTo), _c = _b.next(); !_c.done; _c = _b.next()) {
@@ -736,15 +933,15 @@ function () {
736
933
 
737
934
  child.send(event);
738
935
  }
739
- } catch (e_13_1) {
740
- e_13 = {
741
- error: e_13_1
936
+ } catch (e_12_1) {
937
+ e_12 = {
938
+ error: e_12_1
742
939
  };
743
940
  } finally {
744
941
  try {
745
942
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
746
943
  } finally {
747
- if (e_13) throw e_13.error;
944
+ if (e_12) throw e_12.error;
748
945
  }
749
946
  }
750
947
  };
@@ -771,150 +968,7 @@ function () {
771
968
  actionFunctionMap = this.machine.options.actions;
772
969
  }
773
970
 
774
- var context = state.context,
775
- _event = state._event;
776
- var actionOrExec = action.exec || getActionFunction(action.type, actionFunctionMap);
777
- var exec = isFunction(actionOrExec) ? actionOrExec : actionOrExec ? actionOrExec.exec : action.exec;
778
-
779
- if (exec) {
780
- try {
781
- return exec(context, _event.data, {
782
- action: action,
783
- state: this.state,
784
- _event: _event
785
- });
786
- } catch (err) {
787
- if (this.parent) {
788
- this.parent.send({
789
- type: 'xstate.error',
790
- data: err
791
- });
792
- }
793
-
794
- throw err;
795
- }
796
- }
797
-
798
- switch (action.type) {
799
- case send:
800
- var sendAction = action;
801
-
802
- if (typeof sendAction.delay === 'number') {
803
- this.defer(sendAction);
804
- return;
805
- } else {
806
- if (sendAction.to) {
807
- this.sendTo(sendAction._event, sendAction.to);
808
- } else {
809
- this.send(sendAction._event);
810
- }
811
- }
812
-
813
- break;
814
-
815
- case cancel:
816
- this.cancel(action.sendId);
817
- break;
818
-
819
- case start:
820
- {
821
- if (this.status !== InterpreterStatus.Running) {
822
- return;
823
- }
824
-
825
- var activity = action.activity; // If the activity will be stopped right after it's started
826
- // (such as in transient states)
827
- // don't bother starting the activity.
828
-
829
- if (!this.state.activities[activity.id || activity.type]) {
830
- break;
831
- } // Invoked services
832
-
833
-
834
- if (activity.type === ActionTypes.Invoke) {
835
- var invokeSource = toInvokeSource(activity.src);
836
- var serviceCreator = this.machine.options.services ? this.machine.options.services[invokeSource.type] : undefined;
837
- var id = activity.id,
838
- data = activity.data;
839
-
840
- if (!IS_PRODUCTION) {
841
- warn(!('forward' in activity), // tslint:disable-next-line:max-line-length
842
- "`forward` property is deprecated (found in invocation of '".concat(activity.src, "' in in machine '").concat(this.machine.id, "'). ") + "Please use `autoForward` instead.");
843
- }
844
-
845
- var autoForward = 'autoForward' in activity ? activity.autoForward : !!activity.forward;
846
-
847
- if (!serviceCreator) {
848
- // tslint:disable-next-line:no-console
849
- if (!IS_PRODUCTION) {
850
- warn(false, "No service found for invocation '".concat(activity.src, "' in machine '").concat(this.machine.id, "'."));
851
- }
852
-
853
- return;
854
- }
855
-
856
- var resolvedData = data ? mapContext(data, context, _event) : undefined;
857
-
858
- if (typeof serviceCreator === 'string') {
859
- // TODO: warn
860
- return;
861
- }
862
-
863
- var source = isFunction(serviceCreator) ? serviceCreator(context, _event.data, {
864
- data: resolvedData,
865
- src: invokeSource,
866
- meta: activity.meta
867
- }) : serviceCreator;
868
-
869
- if (!source) {
870
- // TODO: warn?
871
- return;
872
- }
873
-
874
- var options = void 0;
875
-
876
- if (isMachine(source)) {
877
- source = resolvedData ? source.withContext(resolvedData) : source;
878
- options = {
879
- autoForward: autoForward
880
- };
881
- }
882
-
883
- this.spawn(source, id, options);
884
- } else {
885
- this.spawnActivity(activity);
886
- }
887
-
888
- break;
889
- }
890
-
891
- case stop:
892
- {
893
- this.stopChild(action.activity.id);
894
- break;
895
- }
896
-
897
- case log:
898
- var label = action.label,
899
- value = action.value;
900
-
901
- if (label) {
902
- this.logger(label, value);
903
- } else {
904
- this.logger(value);
905
- }
906
-
907
- break;
908
-
909
- default:
910
- if (!IS_PRODUCTION) {
911
- warn(false, "No implementation found for action type '".concat(action.type, "'"));
912
- }
913
-
914
- break;
915
- }
916
-
917
- return undefined;
971
+ this._exec(action, state.context, state._event, actionFunctionMap);
918
972
  };
919
973
 
920
974
  Interpreter.prototype.removeChild = function (childId) {
@@ -942,6 +996,10 @@ function () {
942
996
  };
943
997
 
944
998
  Interpreter.prototype.spawn = function (entity, name, options) {
999
+ if (this.status !== InterpreterStatus.Running) {
1000
+ return createDeferredActor(entity, name);
1001
+ }
1002
+
945
1003
  if (isPromiseLike(entity)) {
946
1004
  return this.spawnPromise(Promise.resolve(entity), name);
947
1005
  } else if (isFunction(entity)) {