From 38d713379f976b07550a72d4deb06312be75903c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 11 Nov 2015 09:55:20 +0100 Subject: [PATCH] :art: Introduce Config::transactAsync --- spec/config-spec.coffee | 43 ++++++++++++++++++++++++++++++++++++++ src/config.coffee | 27 ++++++++++++++++++++++++ src/package-manager.coffee | 12 +++++------ 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index bb9ab89a8..766d0e1e5 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -589,6 +589,49 @@ describe "Config", -> atom.config.transact -> expect(changeSpy).not.toHaveBeenCalled() + describe ".transactAsync(callback)", -> + changeSpy = null + + beforeEach -> + changeSpy = jasmine.createSpy('onDidChange callback') + atom.config.onDidChange("foo.bar.baz", changeSpy) + + it "allows only one change event for the duration of the given promise if it gets resolved", -> + waitsForPromise -> + atom.config.transactAsync -> + atom.config.set("foo.bar.baz", 1) + atom.config.set("foo.bar.baz", 2) + atom.config.set("foo.bar.baz", 3) + Promise.resolve() + + runs -> + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) + + it "allows only one change event for the duration of the given promise if it gets rejected", -> + waitsForPromise shouldReject: true, -> + atom.config.transactAsync -> + atom.config.set("foo.bar.baz", 1) + atom.config.set("foo.bar.baz", 2) + atom.config.set("foo.bar.baz", 3) + Promise.reject() + + runs -> + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) + + it "allows only one change event even when the given callback throws", -> + waitsForPromise shouldReject: true, -> + atom.config.transactAsync -> + atom.config.set("foo.bar.baz", 1) + atom.config.set("foo.bar.baz", 2) + atom.config.set("foo.bar.baz", 3) + throw new Error("Oops!") + + runs -> + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) + describe ".getSources()", -> it "returns an array of all of the config's source names", -> expect(atom.config.getSources()).toEqual([]) diff --git a/src/config.coffee b/src/config.coffee index 372e745c4..bf2a7eae1 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -677,6 +677,33 @@ class Config finally @endTransaction() + # Extended: Suppress calls to handler functions registered with + # {::onDidChange} and {::observe} for the duration of the {Promise} returned + # by `callback`. After the {Promise} is either resolved or rejected, handlers + # will be called once if the value for their key-path has changed. + # + # * `callback` {Function} that returns a {Promise}, which will be executed + # while suppressing calls to handlers. + # + # Returns a {Promise} that is either resolved or rejected according to the + # `{Promise}` returned by `callback`. If `callback` throws an error, a + # rejected {Promise} will be returned. + transactAsync: (callback) -> + @beginTransaction() + endTransaction = (resolveOrReject) => (args...) => + @endTransaction() + resolveOrReject(args...) + + try + result = callback() + new Promise (resolve, reject) => + result + .then(endTransaction(resolve)) + .catch(endTransaction(reject)) + catch error + @endTransaction() + Promise.reject(error) + beginTransaction: -> @transactDepth++ diff --git a/src/package-manager.coffee b/src/package-manager.coffee index d24004031..f99b8f8ed 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -418,13 +418,11 @@ class PackageManager activatePackages: (packages) -> promises = [] - @config.beginTransaction() - for pack in packages - promise = @activatePackage(pack.name) - promises.push(promise) unless pack.activationShouldBeDeferred() - Promise.all(promises) - .then(=> @config.endTransaction()) - .catch(=> @config.endTransaction()) + @config.transactAsync => + for pack in packages + promise = @activatePackage(pack.name) + promises.push(promise) unless pack.activationShouldBeDeferred() + Promise.all(promises) @observeDisabledPackages() @observePackagesWithKeymapsDisabled() promises