33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { Disposable } from 'vs/base/common/lifecycle' ;
6+ import { Disposable , IDisposable , dispose } from 'vs/base/common/lifecycle' ;
77import { Event , Emitter } from 'vs/base/common/event' ;
88import { IWorkspaceStorageChangeEvent , IStorageService , StorageScope , IWillSaveStateEvent , WillSaveStateReason , logStorage } from 'vs/platform/storage/common/storage' ;
99import { IEnvironmentService } from 'vs/platform/environment/common/environment' ;
@@ -21,11 +21,11 @@ export class BrowserStorageService extends Disposable implements IStorageService
2121
2222 _serviceBrand : undefined ;
2323
24- private readonly _onDidChangeStorage : Emitter < IWorkspaceStorageChangeEvent > = this . _register ( new Emitter < IWorkspaceStorageChangeEvent > ( ) ) ;
25- readonly onDidChangeStorage : Event < IWorkspaceStorageChangeEvent > = this . _onDidChangeStorage . event ;
24+ private readonly _onDidChangeStorage = this . _register ( new Emitter < IWorkspaceStorageChangeEvent > ( ) ) ;
25+ readonly onDidChangeStorage = this . _onDidChangeStorage . event ;
2626
27- private readonly _onWillSaveState : Emitter < IWillSaveStateEvent > = this . _register ( new Emitter < IWillSaveStateEvent > ( ) ) ;
28- readonly onWillSaveState : Event < IWillSaveStateEvent > = this . _onWillSaveState . event ;
27+ private readonly _onWillSaveState = this . _register ( new Emitter < IWillSaveStateEvent > ( ) ) ;
28+ readonly onWillSaveState = this . _onWillSaveState . event ;
2929
3030 private globalStorage : IStorage | undefined ;
3131 private workspaceStorage : IStorage | undefined ;
@@ -37,45 +37,15 @@ export class BrowserStorageService extends Disposable implements IStorageService
3737 private workspaceStorageFile : URI | undefined ;
3838
3939 private initializePromise : Promise < void > | undefined ;
40- private periodicSaveScheduler = this . _register ( new RunOnceScheduler ( ( ) => this . collectState ( ) , 5000 ) ) ;
4140
42- get hasPendingUpdate ( ) : boolean {
43- return ( ! ! this . globalStorageDatabase && this . globalStorageDatabase . hasPendingUpdate ) || ( ! ! this . workspaceStorageDatabase && this . workspaceStorageDatabase . hasPendingUpdate ) ;
44- }
41+ private readonly periodicFlushScheduler = this . _register ( new RunOnceScheduler ( ( ) => this . doFlushWhenIdle ( ) , 5000 /* every 5s */ ) ) ;
42+ private runWhenIdleDisposable : IDisposable | undefined = undefined ;
4543
4644 constructor (
4745 @IEnvironmentService private readonly environmentService : IEnvironmentService ,
4846 @IFileService private readonly fileService : IFileService
4947 ) {
5048 super ( ) ;
51-
52- // In the browser we do not have support for long running unload sequences. As such,
53- // we cannot ask for saving state in that moment, because that would result in a
54- // long running operation.
55- // Instead, periodically ask customers to save save. The library will be clever enough
56- // to only save state that has actually changed.
57- this . periodicSaveScheduler . schedule ( ) ;
58- }
59-
60- private collectState ( ) : void {
61- runWhenIdle ( ( ) => {
62-
63- // this event will potentially cause new state to be stored
64- // since new state will only be created while the document
65- // has focus, one optimization is to not run this when the
66- // document has no focus, assuming that state has not changed
67- //
68- // another optimization is to not collect more state if we
69- // have a pending update already running which indicates
70- // that the connection is either slow or disconnected and
71- // thus unhealthy.
72- if ( document . hasFocus ( ) && ! this . hasPendingUpdate ) {
73- this . _onWillSaveState . fire ( { reason : WillSaveStateReason . NONE } ) ;
74- }
75-
76- // repeat
77- this . periodicSaveScheduler . schedule ( ) ;
78- } ) ;
7949 }
8050
8151 initialize ( payload : IWorkspaceInitializationPayload ) : Promise < void > {
@@ -109,6 +79,13 @@ export class BrowserStorageService extends Disposable implements IStorageService
10979 this . workspaceStorage . init ( ) ,
11080 this . globalStorage . init ( )
11181 ] ) ;
82+
83+ // In the browser we do not have support for long running unload sequences. As such,
84+ // we cannot ask for saving state in that moment, because that would result in a
85+ // long running operation.
86+ // Instead, periodically ask customers to save save. The library will be clever enough
87+ // to only save state that has actually changed.
88+ this . periodicFlushScheduler . schedule ( ) ;
11289 }
11390
11491 get ( key : string , scope : StorageScope , fallbackValue : string ) : string ;
@@ -156,6 +133,40 @@ export class BrowserStorageService extends Disposable implements IStorageService
156133 throw new Error ( 'Migrating storage is currently unsupported in Web' ) ;
157134 }
158135
136+ private doFlushWhenIdle ( ) : void {
137+
138+ // Dispose any previous idle runner
139+ this . runWhenIdleDisposable = dispose ( this . runWhenIdleDisposable ) ;
140+
141+ // Run when idle
142+ this . runWhenIdleDisposable = runWhenIdle ( ( ) => {
143+
144+ // this event will potentially cause new state to be stored
145+ // since new state will only be created while the document
146+ // has focus, one optimization is to not run this when the
147+ // document has no focus, assuming that state has not changed
148+ //
149+ // another optimization is to not collect more state if we
150+ // have a pending update already running which indicates
151+ // that the connection is either slow or disconnected and
152+ // thus unhealthy.
153+ if ( document . hasFocus ( ) && ! this . hasPendingUpdate ) {
154+ this . flush ( ) ;
155+ }
156+
157+ // repeat
158+ this . periodicFlushScheduler . schedule ( ) ;
159+ } ) ;
160+ }
161+
162+ get hasPendingUpdate ( ) : boolean {
163+ return ( ! ! this . globalStorageDatabase && this . globalStorageDatabase . hasPendingUpdate ) || ( ! ! this . workspaceStorageDatabase && this . workspaceStorageDatabase . hasPendingUpdate ) ;
164+ }
165+
166+ flush ( ) : void {
167+ this . _onWillSaveState . fire ( { reason : WillSaveStateReason . NONE } ) ;
168+ }
169+
159170 close ( ) : void {
160171 // We explicitly do not close our DBs because writing data onBeforeUnload()
161172 // can result in unexpected results. Namely, it seems that - even though this
@@ -167,6 +178,12 @@ export class BrowserStorageService extends Disposable implements IStorageService
167178 // get triggered in this phase.
168179 this . dispose ( ) ;
169180 }
181+
182+ dispose ( ) : void {
183+ this . runWhenIdleDisposable = dispose ( this . runWhenIdleDisposable ) ;
184+
185+ super . dispose ( ) ;
186+ }
170187}
171188
172189export class FileStorageDatabase extends Disposable implements IStorageDatabase {
0 commit comments