Skip to content

Commit 2d02102

Browse files
Emit liveSyncStopped event when LiveSync operation is really stopped (#3011)
In case `stopLiveSync` is called during livesync, we should await the current action and emit `liveSyncStopped` event. At the moment, we have the following behavior: 1. In case stopLiveSync is called with deviceIdentifiers, we emit `liveSyncStopped` immediately for all of them. However there may be pending actions, for example build + deploy for current device. We cannot stop the actions, so you receive `liveSyncStopped` and application is installed/updated on device after that. Fix this by persisting the current action and await it before emitting `liveSyncStopped` event. 2. In case we are currently rebuilding the application and some changes are applied during build, we'll update the `actionsChain`. At this point we'll have several actions in the chain. In case stopLiveSync method is called (without deviceIdentifiers), we will await all pending actions, which will result in awaiting the build, deploy and all other actions and we'll emit the `liveSyncStopped` after that. However we should not execute all pending actions - we should just execute the current one and skip the rest. In order to fix this, just set isStopped to true before awaiting the actions. This way only the current action will be executed.
1 parent ac4ee24 commit 2d02102

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

lib/definitions/livesync.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ interface ILiveSyncProcessInfo {
6565
actionsChain: Promise<any>;
6666
isStopped: boolean;
6767
deviceDescriptors: ILiveSyncDeviceInfo[];
68+
currentSyncAction: Promise<any>;
6869
}
6970

7071
/**

lib/services/livesync/livesync-service.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService {
4444
const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectDir];
4545

4646
if (liveSyncProcessInfo) {
47-
_.each(deviceIdentifiers, deviceId => {
48-
_.remove(liveSyncProcessInfo.deviceDescriptors, descriptor => {
49-
const shouldRemove = descriptor.identifier === deviceId;
50-
if (shouldRemove) {
51-
this.emit(LiveSyncEvents.liveSyncStopped, { projectDir, deviceIdentifier: descriptor.identifier });
52-
}
47+
// In case we are coming from error during livesync, the current action is the one that erred (but we are still executing it),
48+
// so we cannot await it as this will cause infinite loop.
49+
const shouldAwaitPendingOperation = !stopOptions || stopOptions.shouldAwaitAllActions;
5350

54-
return shouldRemove;
55-
});
51+
let removedDeviceIdentifiers: string[] = deviceIdentifiers || [];
52+
53+
_.each(deviceIdentifiers, deviceId => {
54+
removedDeviceIdentifiers = _.remove(liveSyncProcessInfo.deviceDescriptors, descriptor => descriptor.identifier === deviceId)
55+
.map(deviceDescriptor => deviceDescriptor.identifier);
5656
});
5757

5858
// In case deviceIdentifiers are not passed, we should stop the whole LiveSync.
@@ -66,16 +66,12 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService {
6666
}
6767

6868
liveSyncProcessInfo.watcherInfo = null;
69+
liveSyncProcessInfo.isStopped = true;
6970

70-
if (liveSyncProcessInfo.actionsChain && (!stopOptions || stopOptions.shouldAwaitAllActions)) {
71+
if (liveSyncProcessInfo.actionsChain && shouldAwaitPendingOperation) {
7172
await liveSyncProcessInfo.actionsChain;
7273
}
7374

74-
_.each(liveSyncProcessInfo.deviceDescriptors, descriptor => {
75-
this.emit(LiveSyncEvents.liveSyncStopped, { projectDir, deviceIdentifier: descriptor.identifier });
76-
});
77-
78-
liveSyncProcessInfo.isStopped = true;
7975
liveSyncProcessInfo.deviceDescriptors = [];
8076

8177
// Kill typescript watcher
@@ -85,7 +81,14 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService {
8581
projectData
8682
}
8783
});
84+
} else if (liveSyncProcessInfo.currentSyncAction && shouldAwaitPendingOperation) {
85+
await liveSyncProcessInfo.currentSyncAction;
8886
}
87+
88+
// Emit LiveSync stopped when we've really stopped.
89+
_.each(removedDeviceIdentifiers, deviceIdentifier => {
90+
this.emit(LiveSyncEvents.liveSyncStopped, { projectDir, deviceIdentifier });
91+
});
8992
}
9093
}
9194

@@ -139,6 +142,7 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService {
139142
private setLiveSyncProcessInfo(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[]): void {
140143
this.liveSyncProcessesInfo[projectDir] = this.liveSyncProcessesInfo[projectDir] || Object.create(null);
141144
this.liveSyncProcessesInfo[projectDir].actionsChain = this.liveSyncProcessesInfo[projectDir].actionsChain || Promise.resolve();
145+
this.liveSyncProcessesInfo[projectDir].currentSyncAction = this.liveSyncProcessesInfo[projectDir].actionsChain;
142146
this.liveSyncProcessesInfo[projectDir].isStopped = false;
143147

144148
const currentDeviceDescriptors = this.liveSyncProcessesInfo[projectDir].deviceDescriptors || [];
@@ -446,7 +450,8 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService {
446450
if (liveSyncInfo) {
447451
liveSyncInfo.actionsChain = liveSyncInfo.actionsChain.then(async () => {
448452
if (!liveSyncInfo.isStopped) {
449-
const res = await action();
453+
liveSyncInfo.currentSyncAction = action();
454+
const res = await liveSyncInfo.currentSyncAction;
450455
return res;
451456
}
452457
});

0 commit comments

Comments
 (0)