• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brighterscript / #10562

pending completion
#10562

push

web-flow
Show busy spinner for lsp builds (#852)

* Add busy status tracking

* Fix timing issues with semantic tokens

5563 of 6775 branches covered (82.11%)

Branch coverage included in aggregate %.

80 of 80 new or added lines in 4 files covered. (100.0%)

8487 of 9161 relevant lines covered (92.64%)

1623.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.31
/src/Throttler.ts
1
import util from './util';
1✔
2
import { EventEmitter } from 'eventemitter3';
1✔
3
import { Deferred } from './deferred';
1✔
4

5
export class Throttler {
1✔
6
    public constructor(
7
        readonly delay: number
142✔
8
    ) {
9
    }
10

11
    private runningJobPromise;
12
    private pendingJob;
13
    private emitter = new EventEmitter();
142✔
14

15
    private get isIdle() {
16
        return !this.runningJobPromise;
114✔
17
    }
18

19
    public on(eventName: 'idle' | 'run', callback: () => any) {
20
        this.emitter.on(eventName, callback);
1✔
21
        return () => {
1✔
22
            this.emitter.off(eventName, callback);
1✔
23
        };
24
    }
25

26
    /**
27
     * Resolve a promise the next time the event fires
28
     * @param eventName the name of the event to subscribe to
29
     * @param timeout if the event doesn't fire within the specified time, the promise will auto-resolve itself
30
     */
31
    public once(eventName: 'idle' | 'run', timeout?: number) {
32
        const promises: Promise<any>[] = [];
1✔
33

34
        //register a timeout if specified
35
        if (timeout > 0) {
1!
36
            promises.push(util.sleep(timeout));
1✔
37
        }
38

39
        //wait for the event
40
        promises.push(
1✔
41
            new Promise<void>((resolve) => {
42
                const disconnect = this.on(eventName, () => {
1✔
43
                    disconnect();
1✔
44
                    resolve();
1✔
45
                });
46
            })
47
        );
48

49
        return Promise.race(promises);
1✔
50
    }
51

52
    /**
53
     * Wait for the next 'run' event. Or resolve immediately if already running.
54
     * @param timeout after this timeout, the promise resolves even if the 'run' event never fired
55
     */
56
    public async onRunOnce(timeout = 0) {
×
57
        if (!this.isIdle) {
1!
58
            return;
×
59
        }
60
        return this.once('run', timeout);
1✔
61
    }
62

63
    /**
64
     * Get a promise that resolves the next time the throttler becomes idle
65
     */
66
    public async onIdleOnce(resolveImmediatelyIfIdle = true) {
110✔
67
        if (resolveImmediatelyIfIdle && this.isIdle) {
114✔
68
            return Promise.resolve();
2✔
69
        }
70
        const deferred = new Deferred();
112✔
71
        const callback = () => {
112✔
72
            this.emitter.off('idle', callback);
112✔
73
            deferred.resolve();
112✔
74
        };
75
        this.emitter.on('idle', callback);
112✔
76
        return deferred.promise;
112✔
77
    }
78

79
    public onIdle(callback) {
80
        this.emitter.on('idle', callback);
3✔
81
        return () => {
3✔
82
            this.emitter.off('idle', callback);
1✔
83
        };
84
    }
85

86
    /**
87
     * If no job is running, the given job will run.
88
     * If a job is running, this job will be run after the current job finishes.
89
     * If a job is running, and a new job comes in after this one, this one will be discarded in favor of the new one.
90
     */
91
    public async run(job) {
92
        //if there's a running job, store the incoming job
93
        //(overwrite if one already exists)
94
        if (this.runningJobPromise) {
86✔
95
            this.pendingJob = job;
21✔
96
            //queue this job, and resolve when throttler becomes idle
97
            return this.onIdleOnce();
21✔
98
        } else {
99
            //kick off running the job
100
            return this.runInternal(job);
65✔
101
        }
102
    }
103

104
    /**
105
     * Private method to run a job after a delay.
106
     */
107
    private async runInternal(job) {
108
        if (!this.pendingJob) {
85!
109
            this.emitter.emit('run');
85✔
110
        }
111
        this.runningJobPromise = util.sleep(this.delay).then(() => {
85✔
112
            //run the job
113
            return job();
85✔
114
        }).catch((e) => {
115
            //log the error, but keep moving
116
            console.error(e);
1✔
117
        }).then(() => {
118
            //if there's a pending job, run that one now
119
            if (this.pendingJob) {
85✔
120
                //get reference to the pending job
121
                let pendingJob = this.pendingJob;
20✔
122
                //erase the pending job since we're going to run it (it'll be come the active job)
123
                this.pendingJob = undefined;
20✔
124
                return this.runInternal(pendingJob);
20✔
125
            } else {
126
                //there is no pending job
127
                this.emitter.emit('idle');
65✔
128
                this.runningJobPromise = undefined;
65✔
129
            }
130
        });
131
        //resolve when throttler becomes idle
132
        return this.onIdleOnce();
85✔
133
    }
134

135
    public dispose() {
136
        this.emitter.removeAllListeners();
35✔
137
    }
138
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc