1 // OpenTween - Client of Twitter
2 // Copyright (c) 2018 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
17 // You should have received a copy of the GNU General public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
25 using System.Threading;
26 using System.Threading.Tasks;
31 /// コールバック先の関数を <see cref="Interval"/> 未満の頻度で呼ばないよう制御するタイマー
34 /// lodash の <c>_.throttle()</c> に相当する機能となっている
36 public class ThrottleTimer : IDisposable
38 private readonly ITimer throttlingTimer;
39 private readonly Func<Task> timerCallback;
40 private readonly object lockObject = new();
42 private bool calledSinceLastInvoke;
43 private bool refreshTimerEnabled;
45 public TimeSpan Interval { get; }
47 public ThrottleTimer(Func<Task> timerCallback, TimeSpan interval)
49 this.timerCallback = timerCallback;
50 this.Interval = interval;
51 this.throttlingTimer = this.CreateTimer(this.Execute);
52 this.calledSinceLastInvoke = false;
53 this.refreshTimerEnabled = false;
56 protected virtual ITimer CreateTimer(Func<Task> callback)
57 => new AsyncTimer(callback);
59 public async Task Call()
62 lock (this.lockObject)
64 this.calledSinceLastInvoke = true;
65 if (this.refreshTimerEnabled)
72 this.refreshTimerEnabled = true;
78 await this.Invoke().ConfigureAwait(false);
79 this.throttlingTimer.Change(dueTime: this.Interval, period: Timeout.InfiniteTimeSpan);
83 private async Task Execute()
86 lock (this.lockObject)
88 if (this.calledSinceLastInvoke)
95 this.refreshTimerEnabled = false;
101 await this.Invoke().ConfigureAwait(false);
102 this.throttlingTimer.Change(dueTime: this.Interval, period: Timeout.InfiniteTimeSpan);
106 private async Task Invoke()
108 await Task.Run(async () =>
110 lock (this.lockObject)
111 this.calledSinceLastInvoke = false;
113 await this.timerCallback().ConfigureAwait(false);
117 public void Dispose()
118 => this.throttlingTimer.Dispose();
120 public static ThrottleTimer Create(Func<Task> callback, TimeSpan wait)
121 => new(callback, wait);