From e7f4dccfc3de2e3829ee09a5e9b439c9c52660f9 Mon Sep 17 00:00:00 2001 From: Fred Cox Date: Sat, 2 Feb 2019 16:23:16 +0200 Subject: [PATCH] Clear sequentialize cache when empty to remove chance of memory leaks (#26) --- spec/common/misc/sequentialize.spec.ts | 4 ++-- src/misc/sequentialize.ts | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/spec/common/misc/sequentialize.spec.ts b/spec/common/misc/sequentialize.spec.ts index 8bb2701cb2..86e846b38e 100644 --- a/spec/common/misc/sequentialize.spec.ts +++ b/spec/common/misc/sequentialize.spec.ts @@ -120,7 +120,7 @@ class Foo { calls = 0; @sequentialize((args) => 'bar' + args[0]) - bar(a: any) { + bar(a: number) { this.calls++; return new Promise((res) => { setTimeout(() => { @@ -130,7 +130,7 @@ class Foo { } @sequentialize((args) => 'baz' + args[0]) - baz(a: any) { + baz(a: number) { this.calls++; return new Promise((res) => { setTimeout(() => { diff --git a/src/misc/sequentialize.ts b/src/misc/sequentialize.ts index b9501a8ad2..221b9da23b 100644 --- a/src/misc/sequentialize.ts +++ b/src/misc/sequentialize.ts @@ -6,7 +6,6 @@ * * Results are not cached, once the promise has returned, the next call will result in a fresh call * - * WARNING: The decorator's scope is singleton, so using it on transient objects can lead to memory leaks. * Read more at https://github.com/bitwarden/jslib/pull/7 */ export function sequentialize(cacheKey: (args: any[]) => string) { @@ -26,20 +25,20 @@ export function sequentialize(cacheKey: (args: any[]) => string) { return { value: function(...args: any[]) { - const argsCacheKey = cacheKey(args); const cache = getCache(this); + const argsCacheKey = cacheKey(args); let response = cache.get(argsCacheKey); if (response != null) { return response; } - response = originalMethod.apply(this, args).then((val: any) => { - cache.delete(argsCacheKey); - return val; - }).catch((err: any) => { - cache.delete(argsCacheKey); - throw err; - }); + response = originalMethod.apply(this, args) + .finally(() => { + cache.delete(argsCacheKey); + if (cache.size === 0) { + caches.delete(this); + } + }); cache.set(argsCacheKey, response); return response;