[Clipboard API] Implement getType() for ClipboardItems created from bindings
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Base / Throttler.js
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 // Throttler wraps a function and ensures it doesn't get called more than once every `delay` ms.
27 // The first fire will always trigger synchronous, and subsequent calls that need to be throttled
28 // will happen later asynchronously.
29 //
30 // Example:
31 //
32 //     let throttler = new Throttler(250, () => { this.refresh() });
33 //     element.addEventListener("keydown", (event) => { throttler.fire(); });
34 //
35 // Will ensure `refresh` happens no more than once every 250ms no matter how often `fire` is called:
36 //
37 //               0ms                        250ms                       500ms
38 //     time:      |---------------------------|---------------------------|
39 //     fire:      ^     ^ ^        ^      ^        ^    ^   ^
40 //     refreshes: * (1)                       * (2)                       * (3)
41 //
42 // When the wrapped function is actually called, it will be given the most recent set of arguments.
43
44 class Throttler
45 {
46     constructor(callback, delay)
47     {
48         console.assert(typeof callback === "function");
49         console.assert(delay >= 0);
50
51         this._callback = callback;
52         this._delay = delay;
53
54         this._lastArguments = [];
55
56         this._timeoutIdentifier = undefined;
57         this._lastFireTime = -this._delay;
58     }
59
60     // Public
61
62     force()
63     {
64         this._lastArguments = arguments;
65         this._execute();
66     }
67
68     fire()
69     {
70         this._lastArguments = arguments;
71
72         let remaining = this._delay - (Date.now() - this._lastFireTime);
73         if (remaining <= 0) {
74             this._execute();
75             return;
76         }
77
78         if (this._timeoutIdentifier)
79             return;
80
81         this._timeoutIdentifier = setTimeout(() => {
82             this._execute();
83         }, remaining);
84     }
85
86     cancel()
87     {
88         this._lastArguments = [];
89
90         if (this._timeoutIdentifier) {
91             clearTimeout(this._timeoutIdentifier);
92             this._timeoutIdentifier = undefined;
93         }
94     }
95
96     // Private
97
98     _execute()
99     {
100         this._lastFireTime = Date.now();
101
102         let args = this._lastArguments;
103
104         this.cancel();
105
106         this._callback.apply(undefined, args);
107     }
108 }