-
Notifications
You must be signed in to change notification settings - Fork 1
/
debouncePromise.ts
105 lines (95 loc) · 3.44 KB
/
debouncePromise.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
* Copyright 2019 LABOR.digital
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Last modified: 2019.01.25 at 12:17
*/
import {EventBus} from '../Events/EventBus';
interface PromiseCreator
{
(): Promise<any>
}
interface PromiseRegistryEntry
{
event: string;
timeout: number | any;
guid: number;
promise: null | Promise<any>
}
const registry: Map<string, PromiseRegistryEntry> = new Map();
/**
* Similar to throttleEvent but debounces a promise creating callback.
* As long as the function gets invoked the promiseCreator will not be executed
* If the delay of limit milliseconds is passed without the function beeing called,
* the promiseCreator will be executed and tho the real promise created.
*
* All resulting .then() functions will receive the result of the last promise request.
*
* @param key A unique key to define a unique promise group
* @param promiseCreator The function which should only be called after the timeout passed
* @param limit The timeout in milliseconds before the promise should be called
* @param skipAllButLast If this is set to true only the last .then() will be executed.
* Otherwise all .then() methods will be executed, but receive the same result
*/
export function debouncePromise(key: string, promiseCreator: PromiseCreator,
limit: number, skipAllButLast?: boolean
): Promise<any>
{
// Initialize the wrapper if required
if (!registry.has(key)) {
registry.set(key, {
event: 'debouncePromise__timeout--' + key,
timeout: 0,
guid: 0,
promise: null
});
}
// Load config
const config = registry.get(key)!;
// Register timeout
clearTimeout(config.timeout);
config.timeout = setTimeout(() => {
EventBus.emit(config.event, {promiseCreator});
}, limit);
return new Promise((resolve, reject) => {
const localGuid = ++config.guid;
const callback = function (e: any) {
// Clean our callback
EventBus.unbind(config.event, callback);
// Check if this is the last promise
if (localGuid === config.guid) {
registry.delete(key);
} else if (skipAllButLast === true) {
return reject('@skipAllButLast');
}
// Create or pass the real promise
if (config.promise === null) {
config.promise = e.args.promiseCreator();
}
config.promise!.then(resolve).catch(reject);
};
EventBus.bind(config.event, callback);
}).catch((e) => {
// Check if we should skip all other executions
if (e === '@skipAllButLast') {
const noopPromise = {
finally: () => noopPromise,
then: () => noopPromise,
catch: () => noopPromise
};
return noopPromise;
}
return Promise.reject(e);
});
}