async.ts 17.9 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
J
Joao Moreno 已提交
5

E
Erich Gamma 已提交
6 7
'use strict';

J
Johannes Rieken 已提交
8
import * as errors from 'vs/base/common/errors';
9
import { TPromise, ValueCallback, ErrorCallback, ProgressCallback } from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
10
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
11
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
M
Matt Bierner 已提交
12
import { Event, Emitter } from 'vs/base/common/event';
13
import URI from 'vs/base/common/uri';
14

15
export function isThenable<T>(obj: any): obj is Thenable<T> {
16 17 18
	return obj && typeof (<Thenable<any>>obj).then === 'function';
}

19 20 21 22 23 24 25 26
export function toThenable<T>(arg: T | Thenable<T>): Thenable<T> {
	if (isThenable(arg)) {
		return arg;
	} else {
		return TPromise.as(arg);
	}
}

B
Benjamin Pasero 已提交
27 28 29 30 31 32 33 34
export function toWinJsPromise<T>(arg: Thenable<T> | TPromise<T>): TPromise<T> {
	if (arg instanceof TPromise) {
		return arg;
	}

	return new TPromise((resolve, reject) => arg.then(resolve, reject));
}

35
export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | TPromise<T> | Thenable<T>): TPromise<T> {
36
	let source = new CancellationTokenSource();
37
	return new TPromise<T>((resolve, reject, progress) => {
38
		let item = callback(source.token);
39
		if (item instanceof TPromise) {
J
Johannes Rieken 已提交
40 41 42 43 44 45 46
			item.then(result => {
				source.dispose();
				resolve(result);
			}, err => {
				source.dispose();
				reject(err);
			}, progress);
47
		} else if (isThenable<T>(item)) {
J
Johannes Rieken 已提交
48 49 50 51 52 53 54
			item.then(result => {
				source.dispose();
				resolve(result);
			}, err => {
				source.dispose();
				reject(err);
			});
55
		} else {
J
Johannes Rieken 已提交
56
			source.dispose();
57 58 59 60 61 62
			resolve(item);
		}
	}, () => {
		source.cancel();
	});
}
E
Erich Gamma 已提交
63

64 65 66
/**
 * Hook a cancellation token to a WinJS Promise
 */
67
export function wireCancellationToken<T>(token: CancellationToken, promise: TPromise<T>, resolveAsUndefinedWhenCancelled?: boolean): Thenable<T> {
68
	const subscription = token.onCancellationRequested(() => promise.cancel());
69
	if (resolveAsUndefinedWhenCancelled) {
70
		promise = promise.then<T>(undefined, err => {
71 72 73
			if (!errors.isPromiseCanceledError(err)) {
				return TPromise.wrapError(err);
			}
M
Matt Bierner 已提交
74
			return undefined;
75 76
		});
	}
77
	return always(promise, () => subscription.dispose());
78 79
}

80 81 82 83 84
export function asDisposablePromise<T>(input: Thenable<T>, cancelValue?: T, bucket?: IDisposable[]): { promise: Thenable<T> } & IDisposable {
	let dispose: () => void;
	let promise = new TPromise((resolve, reject) => {
		dispose = function () {
			resolve(cancelValue);
85 86 87
			if (isWinJSPromise(input)) {
				input.cancel();
			}
88
		};
89 90 91 92 93 94 95
		input.then(resolve, err => {
			if (errors.isPromiseCanceledError(err)) {
				resolve(cancelValue);
			} else {
				reject(err);
			}
		});
96 97 98 99 100 101 102 103 104 105 106
	});
	let res = {
		promise,
		dispose
	};
	if (Array.isArray(bucket)) {
		bucket.push(res);
	}
	return res;
}

E
Erich Gamma 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
export interface ITask<T> {
	(): T;
}

/**
 * A helper to prevent accumulation of sequential async tasks.
 *
 * Imagine a mail man with the sole task of delivering letters. As soon as
 * a letter submitted for delivery, he drives to the destination, delivers it
 * and returns to his base. Imagine that during the trip, N more letters were submitted.
 * When the mail man returns, he picks those N letters and delivers them all in a
 * single trip. Even though N+1 submissions occurred, only 2 deliveries were made.
 *
 * The throttler implements this via the queue() method, by providing it a task
 * factory. Following the example:
 *
B
Benjamin Pasero 已提交
123 124
 * 		const throttler = new Throttler();
 * 		const letters = [];
E
Erich Gamma 已提交
125
 *
J
Joao Moreno 已提交
126 127 128 129 130 131 132
 * 		function deliver() {
 * 			const lettersToDeliver = letters;
 * 			letters = [];
 * 			return makeTheTrip(lettersToDeliver);
 * 		}
 *
 * 		function onLetterReceived(l) {
E
Erich Gamma 已提交
133
 * 			letters.push(l);
J
Joao Moreno 已提交
134
 * 			throttler.queue(deliver);
E
Erich Gamma 已提交
135 136 137 138
 * 		}
 */
export class Throttler {

139 140 141
	private activePromise: TPromise;
	private queuedPromise: TPromise;
	private queuedPromiseFactory: ITask<TPromise>;
E
Erich Gamma 已提交
142 143 144 145 146 147 148

	constructor() {
		this.activePromise = null;
		this.queuedPromise = null;
		this.queuedPromiseFactory = null;
	}

J
Joao Moreno 已提交
149
	queue<T>(promiseFactory: ITask<TPromise<T>>): TPromise<T> {
E
Erich Gamma 已提交
150 151 152 153
		if (this.activePromise) {
			this.queuedPromiseFactory = promiseFactory;

			if (!this.queuedPromise) {
J
Johannes Rieken 已提交
154
				const onComplete = () => {
E
Erich Gamma 已提交
155 156
					this.queuedPromise = null;

J
Johannes Rieken 已提交
157
					const result = this.queue(this.queuedPromiseFactory);
E
Erich Gamma 已提交
158 159 160 161 162
					this.queuedPromiseFactory = null;

					return result;
				};

163
				this.queuedPromise = new TPromise((c, e, p) => {
E
Erich Gamma 已提交
164 165 166 167 168 169
					this.activePromise.then(onComplete, onComplete, p).done(c);
				}, () => {
					this.activePromise.cancel();
				});
			}

170
			return new TPromise((c, e, p) => {
E
Erich Gamma 已提交
171 172 173 174 175 176 177 178
				this.queuedPromise.then(c, e, p);
			}, () => {
				// no-op
			});
		}

		this.activePromise = promiseFactory();

179
		return new TPromise((c, e, p) => {
E
Erich Gamma 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192
			this.activePromise.done((result: any) => {
				this.activePromise = null;
				c(result);
			}, (err: any) => {
				this.activePromise = null;
				e(err);
			}, p);
		}, () => {
			this.activePromise.cancel();
		});
	}
}

193 194 195
// TODO@Joao: can the previous throttler be replaced with this?
export class SimpleThrottler {

196
	private current = TPromise.wrap<any>(null);
197 198 199 200 201 202

	queue<T>(promiseTask: ITask<TPromise<T>>): TPromise<T> {
		return this.current = this.current.then(() => promiseTask());
	}
}

E
Erich Gamma 已提交
203 204 205 206
/**
 * A helper to delay execution of a task that is being requested often.
 *
 * Following the throttler, now imagine the mail man wants to optimize the number of
207
 * trips proactively. The trip itself can be long, so he decides not to make the trip
E
Erich Gamma 已提交
208 209 210 211 212 213 214 215 216 217
 * as soon as a letter is submitted. Instead he waits a while, in case more
 * letters are submitted. After said waiting period, if no letters were submitted, he
 * decides to make the trip. Imagine that N more letters were submitted after the first
 * one, all within a short period of time between each other. Even though N+1
 * submissions occurred, only 1 delivery was made.
 *
 * The delayer offers this behavior via the trigger() method, into which both the task
 * to be executed and the waiting period (delay) must be passed in as arguments. Following
 * the example:
 *
B
Benjamin Pasero 已提交
218 219
 * 		const delayer = new Delayer(WAITING_PERIOD);
 * 		const letters = [];
E
Erich Gamma 已提交
220 221 222 223 224 225 226 227 228
 *
 * 		function letterReceived(l) {
 * 			letters.push(l);
 * 			delayer.trigger(() => { return makeTheTrip(); });
 * 		}
 */
export class Delayer<T> {

	private timeout: number;
229
	private completionPromise: TPromise;
E
Erich Gamma 已提交
230
	private onSuccess: ValueCallback;
231
	private task: ITask<T | TPromise<T>>;
E
Erich Gamma 已提交
232

J
Joao Moreno 已提交
233
	constructor(public defaultDelay: number) {
E
Erich Gamma 已提交
234 235 236 237 238 239
		this.timeout = null;
		this.completionPromise = null;
		this.onSuccess = null;
		this.task = null;
	}

240
	trigger(task: ITask<T | TPromise<T>>, delay: number = this.defaultDelay): TPromise<T> {
E
Erich Gamma 已提交
241 242 243 244
		this.task = task;
		this.cancelTimeout();

		if (!this.completionPromise) {
245
			this.completionPromise = new TPromise((c) => {
E
Erich Gamma 已提交
246 247 248 249 250 251
				this.onSuccess = c;
			}, () => {
				// no-op
			}).then(() => {
				this.completionPromise = null;
				this.onSuccess = null;
J
Joao Moreno 已提交
252
				const task = this.task;
E
Erich Gamma 已提交
253 254
				this.task = null;

J
Joao Moreno 已提交
255
				return task();
E
Erich Gamma 已提交
256 257 258 259 260 261 262 263 264 265 266
			});
		}

		this.timeout = setTimeout(() => {
			this.timeout = null;
			this.onSuccess(null);
		}, delay);

		return this.completionPromise;
	}

J
Joao Moreno 已提交
267
	isTriggered(): boolean {
E
Erich Gamma 已提交
268 269 270
		return this.timeout !== null;
	}

J
Joao Moreno 已提交
271
	cancel(): void {
E
Erich Gamma 已提交
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
		this.cancelTimeout();

		if (this.completionPromise) {
			this.completionPromise.cancel();
			this.completionPromise = null;
		}
	}

	private cancelTimeout(): void {
		if (this.timeout !== null) {
			clearTimeout(this.timeout);
			this.timeout = null;
		}
	}
}

/**
 * A helper to delay execution of a task that is being requested often, while
 * preventing accumulation of consecutive executions, while the task runs.
 *
292
 * Simply combine the two mail men's strategies from the Throttler and Delayer
E
Erich Gamma 已提交
293 294
 * helpers, for an analogy.
 */
J
Joao Moreno 已提交
295
export class ThrottledDelayer<T> extends Delayer<TPromise<T>> {
E
Erich Gamma 已提交
296 297 298 299 300 301 302 303 304

	private throttler: Throttler;

	constructor(defaultDelay: number) {
		super(defaultDelay);

		this.throttler = new Throttler();
	}

305
	trigger(promiseFactory: ITask<TPromise<T>>, delay?: number): TPromise {
E
Erich Gamma 已提交
306 307 308 309
		return super.trigger(() => this.throttler.queue(promiseFactory), delay);
	}
}

J
Joao Moreno 已提交
310 311 312 313
/**
 * A barrier that is initially closed and then becomes opened permanently.
 */
export class Barrier {
E
Erich Gamma 已提交
314

J
Joao Moreno 已提交
315 316 317
	private _isOpen: boolean;
	private _promise: TPromise<boolean>;
	private _completePromise: (v: boolean) => void;
E
Erich Gamma 已提交
318 319

	constructor() {
J
Joao Moreno 已提交
320 321 322 323 324
		this._isOpen = false;
		this._promise = new TPromise<boolean>((c, e, p) => {
			this._completePromise = c;
		}, () => {
			console.warn('You should really not try to cancel this ready promise!');
E
Erich Gamma 已提交
325 326 327
		});
	}

J
Joao Moreno 已提交
328 329
	isOpen(): boolean {
		return this._isOpen;
E
Erich Gamma 已提交
330 331
	}

J
Joao Moreno 已提交
332 333 334
	open(): void {
		this._isOpen = true;
		this._completePromise(true);
E
Erich Gamma 已提交
335 336
	}

J
Joao Moreno 已提交
337 338
	wait(): TPromise<boolean> {
		return this._promise;
E
Erich Gamma 已提交
339 340 341 342 343 344 345
	}
}

export class ShallowCancelThenPromise<T> extends TPromise<T> {

	constructor(outer: TPromise<T>) {

J
Johannes Rieken 已提交
346
		let completeCallback: ValueCallback,
E
Erich Gamma 已提交
347 348 349 350
			errorCallback: ErrorCallback,
			progressCallback: ProgressCallback;

		super((c, e, p) => {
J
Joao Moreno 已提交
351
			completeCallback = c;
E
Erich Gamma 已提交
352 353 354 355 356 357 358 359 360 361 362 363
			errorCallback = e;
			progressCallback = p;
		}, () => {
			// cancel this promise but not the
			// outer promise
			errorCallback(errors.canceled());
		});

		outer.then(completeCallback, errorCallback, progressCallback);
	}
}

364
/**
365
 * Replacement for `WinJS.TPromise.timeout`.
366
 */
367 368
export function timeout(n: number): Thenable<void> {
	return new TPromise(resolve => setTimeout(resolve, n));
369 370
}

J
Johannes Rieken 已提交
371 372 373 374
function isWinJSPromise(candidate: any): candidate is TPromise {
	return TPromise.is(candidate) && typeof (<TPromise>candidate).done === 'function';
}

E
Erich Gamma 已提交
375 376 377 378 379 380 381
/**
 * Returns a new promise that joins the provided promise. Upon completion of
 * the provided promise the provided function will always be called. This
 * method is comparable to a try-finally code block.
 * @param promise a promise
 * @param f a function that will be call in the success and error case.
 */
J
Johannes Rieken 已提交
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
export function always<T>(thenable: TPromise<T>, f: Function): TPromise<T>;
export function always<T>(promise: Thenable<T>, f: Function): Thenable<T>;
export function always<T>(winjsPromiseOrThenable: Thenable<T> | TPromise<T>, f: Function): TPromise<T> | Thenable<T> {
	if (isWinJSPromise(winjsPromiseOrThenable)) {
		return new TPromise<T>((c, e, p) => {
			winjsPromiseOrThenable.done((result) => {
				try {
					f(result);
				} catch (e1) {
					errors.onUnexpectedError(e1);
				}
				c(result);
			}, (err) => {
				try {
					f(err);
				} catch (e1) {
					errors.onUnexpectedError(e1);
				}
				e(err);
			}, (progress) => {
				p(progress);
			});
		}, () => {
			winjsPromiseOrThenable.cancel();
E
Erich Gamma 已提交
406
		});
J
Johannes Rieken 已提交
407 408 409 410 411 412

	} else {
		// simple
		winjsPromiseOrThenable.then(_ => f(), _ => f());
		return winjsPromiseOrThenable;
	}
E
Erich Gamma 已提交
413 414 415 416 417 418
}

/**
 * Runs the provided list of promise factories in sequential order. The returned
 * promise will complete to an array of results from each promise.
 */
419 420

export function sequence<T>(promiseFactories: ITask<Thenable<T>>[]): TPromise<T[]> {
J
Johannes Rieken 已提交
421
	const results: T[] = [];
422 423
	let index = 0;
	const len = promiseFactories.length;
E
Erich Gamma 已提交
424

425 426
	function next(): Thenable<T> | null {
		return index < len ? promiseFactories[index++]() : null;
E
Erich Gamma 已提交
427 428
	}

429
	function thenHandler(result: any): Thenable<any> {
430
		if (result !== undefined && result !== null) {
E
Erich Gamma 已提交
431 432 433
			results.push(result);
		}

J
Johannes Rieken 已提交
434
		const n = next();
E
Erich Gamma 已提交
435 436 437 438 439 440 441
		if (n) {
			return n.then(thenHandler);
		}

		return TPromise.as(results);
	}

A
Alex Dima 已提交
442
	return TPromise.as(null).then(thenHandler);
E
Erich Gamma 已提交
443 444
}

J
Joao Moreno 已提交
445
export function first<T>(promiseFactories: ITask<TPromise<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T = null): TPromise<T> {
446 447
	let index = 0;
	const len = promiseFactories.length;
J
Joao Moreno 已提交
448

B
Benjamin Pasero 已提交
449
	const loop: () => TPromise<T> = () => {
450
		if (index >= len) {
J
Joao Moreno 已提交
451
			return TPromise.as(defaultValue);
J
Joao Moreno 已提交
452 453
		}

454
		const factory = promiseFactories[index++];
J
Joao Moreno 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467 468
		const promise = factory();

		return promise.then(result => {
			if (shouldStop(result)) {
				return TPromise.as(result);
			}

			return loop();
		});
	};

	return loop();
}

E
Erich Gamma 已提交
469
interface ILimitedTaskFactory {
470
	factory: ITask<TPromise>;
E
Erich Gamma 已提交
471 472 473 474 475 476 477 478 479 480
	c: ValueCallback;
	e: ErrorCallback;
	p: ProgressCallback;
}

/**
 * A helper to queue N promises and run them all with a max degree of parallelism. The helper
 * ensures that at any time no more than M promises are running at the same time.
 */
export class Limiter<T> {
I
isidor 已提交
481
	private runningPromises: number;
E
Erich Gamma 已提交
482 483
	private maxDegreeOfParalellism: number;
	private outstandingPromises: ILimitedTaskFactory[];
M
Matt Bierner 已提交
484
	private readonly _onFinished: Emitter<void>;
E
Erich Gamma 已提交
485 486 487 488

	constructor(maxDegreeOfParalellism: number) {
		this.maxDegreeOfParalellism = maxDegreeOfParalellism;
		this.outstandingPromises = [];
I
isidor 已提交
489
		this.runningPromises = 0;
490 491 492 493 494
		this._onFinished = new Emitter<void>();
	}

	public get onFinished(): Event<void> {
		return this._onFinished.event;
E
Erich Gamma 已提交
495 496
	}

497
	public get size(): number {
I
isidor 已提交
498
		return this.runningPromises + this.outstandingPromises.length;
499 500
	}

501
	queue(promiseFactory: ITask<TPromise>): TPromise;
J
Joao Moreno 已提交
502
	queue(promiseFactory: ITask<TPromise<T>>): TPromise<T> {
E
Erich Gamma 已提交
503 504 505 506 507 508 509 510 511 512 513 514 515
		return new TPromise<T>((c, e, p) => {
			this.outstandingPromises.push({
				factory: promiseFactory,
				c: c,
				e: e,
				p: p
			});

			this.consume();
		});
	}

	private consume(): void {
I
isidor 已提交
516
		while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
J
Johannes Rieken 已提交
517
			const iLimitedTask = this.outstandingPromises.shift();
I
isidor 已提交
518
			this.runningPromises++;
E
Erich Gamma 已提交
519

J
Johannes Rieken 已提交
520
			const promise = iLimitedTask.factory();
E
Erich Gamma 已提交
521
			promise.done(iLimitedTask.c, iLimitedTask.e, iLimitedTask.p);
I
isidor 已提交
522
			promise.done(() => this.consumed(), () => this.consumed());
E
Erich Gamma 已提交
523 524 525
		}
	}

I
isidor 已提交
526 527
	private consumed(): void {
		this.runningPromises--;
528 529 530 531 532 533 534 535 536 537

		if (this.outstandingPromises.length > 0) {
			this.consume();
		} else {
			this._onFinished.fire();
		}
	}

	public dispose(): void {
		this._onFinished.dispose();
E
Erich Gamma 已提交
538 539 540
	}
}

B
Benjamin Pasero 已提交
541 542 543 544 545 546 547 548 549 550
/**
 * A queue is handles one promise at a time and guarantees that at any time only one promise is executing.
 */
export class Queue<T> extends Limiter<T> {

	constructor() {
		super(1);
	}
}

551 552 553 554
/**
 * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource
 * by disposing them once the queue is empty.
 */
B
Benjamin Pasero 已提交
555
export class ResourceQueue {
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
	private queues: { [path: string]: Queue<void> };

	constructor() {
		this.queues = Object.create(null);
	}

	public queueFor(resource: URI): Queue<void> {
		const key = resource.toString();
		if (!this.queues[key]) {
			const queue = new Queue<void>();
			queue.onFinished(() => {
				queue.dispose();
				delete this.queues[key];
			});

			this.queues[key] = queue;
		}

		return this.queues[key];
	}
}

578 579 580 581 582
export function setDisposableTimeout(handler: Function, timeout: number, ...args: any[]): IDisposable {
	const handle = setTimeout(handler, timeout, ...args);
	return { dispose() { clearTimeout(handle); } };
}

A
Alex Dima 已提交
583
export class TimeoutTimer extends Disposable {
A
Alex Dima 已提交
584
	private _token: number;
A
Alex Dima 已提交
585 586 587 588 589 590

	constructor() {
		super();
		this._token = -1;
	}

J
Joao Moreno 已提交
591
	dispose(): void {
A
Alex Dima 已提交
592 593 594 595
		this.cancel();
		super.dispose();
	}

J
Joao Moreno 已提交
596
	cancel(): void {
A
Alex Dima 已提交
597
		if (this._token !== -1) {
A
Alex Dima 已提交
598
			clearTimeout(this._token);
A
Alex Dima 已提交
599 600 601 602
			this._token = -1;
		}
	}

J
Johannes Rieken 已提交
603
	cancelAndSet(runner: () => void, timeout: number): void {
A
Alex Dima 已提交
604
		this.cancel();
A
Alex Dima 已提交
605
		this._token = setTimeout(() => {
A
Alex Dima 已提交
606 607 608 609 610
			this._token = -1;
			runner();
		}, timeout);
	}

J
Joao Moreno 已提交
611
	setIfNotSet(runner: () => void, timeout: number): void {
A
Alex Dima 已提交
612 613 614 615
		if (this._token !== -1) {
			// timer is already set
			return;
		}
A
Alex Dima 已提交
616
		this._token = setTimeout(() => {
A
Alex Dima 已提交
617 618 619 620 621 622 623
			this._token = -1;
			runner();
		}, timeout);
	}
}

export class IntervalTimer extends Disposable {
J
Joao Moreno 已提交
624

A
Alex Dima 已提交
625
	private _token: number;
A
Alex Dima 已提交
626 627 628 629 630 631

	constructor() {
		super();
		this._token = -1;
	}

J
Joao Moreno 已提交
632
	dispose(): void {
A
Alex Dima 已提交
633 634 635 636
		this.cancel();
		super.dispose();
	}

J
Joao Moreno 已提交
637
	cancel(): void {
A
Alex Dima 已提交
638
		if (this._token !== -1) {
A
Alex Dima 已提交
639
			clearInterval(this._token);
A
Alex Dima 已提交
640 641 642 643
			this._token = -1;
		}
	}

J
Johannes Rieken 已提交
644
	cancelAndSet(runner: () => void, interval: number): void {
A
Alex Dima 已提交
645
		this.cancel();
A
Alex Dima 已提交
646
		this._token = setInterval(() => {
A
Alex Dima 已提交
647 648 649 650 651
			runner();
		}, interval);
	}
}

E
Erich Gamma 已提交
652 653
export class RunOnceScheduler {

654 655
	protected runner: (...args: any[]) => void;

A
Alex Dima 已提交
656
	private timeoutToken: number;
E
Erich Gamma 已提交
657 658 659
	private timeout: number;
	private timeoutHandler: () => void;

660
	constructor(runner: (...args: any[]) => void, timeout: number) {
E
Erich Gamma 已提交
661 662 663 664 665 666 667 668 669
		this.timeoutToken = -1;
		this.runner = runner;
		this.timeout = timeout;
		this.timeoutHandler = this.onTimeout.bind(this);
	}

	/**
	 * Dispose RunOnceScheduler
	 */
J
Joao Moreno 已提交
670
	dispose(): void {
E
Erich Gamma 已提交
671 672 673 674 675
		this.cancel();
		this.runner = null;
	}

	/**
676
	 * Cancel current scheduled runner (if any).
E
Erich Gamma 已提交
677
	 */
J
Joao Moreno 已提交
678
	cancel(): void {
I
isidor 已提交
679
		if (this.isScheduled()) {
A
Alex Dima 已提交
680
			clearTimeout(this.timeoutToken);
E
Erich Gamma 已提交
681 682 683 684 685 686 687
			this.timeoutToken = -1;
		}
	}

	/**
	 * Cancel previous runner (if any) & schedule a new runner.
	 */
J
Joao Moreno 已提交
688
	schedule(delay = this.timeout): void {
E
Erich Gamma 已提交
689
		this.cancel();
A
Alex Dima 已提交
690
		this.timeoutToken = setTimeout(this.timeoutHandler, delay);
E
Erich Gamma 已提交
691 692
	}

I
isidor 已提交
693 694 695
	/**
	 * Returns true if scheduled.
	 */
J
Joao Moreno 已提交
696
	isScheduled(): boolean {
I
isidor 已提交
697 698 699
		return this.timeoutToken !== -1;
	}

E
Erich Gamma 已提交
700 701 702
	private onTimeout() {
		this.timeoutToken = -1;
		if (this.runner) {
703
			this.doRun();
E
Erich Gamma 已提交
704 705
		}
	}
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738

	protected doRun(): void {
		this.runner();
	}
}

export class RunOnceWorker<T> extends RunOnceScheduler {
	private units: T[] = [];

	constructor(runner: (units: T[]) => void, timeout: number) {
		super(runner, timeout);
	}

	work(unit: T): void {
		this.units.push(unit);

		if (!this.isScheduled()) {
			this.schedule();
		}
	}

	protected doRun(): void {
		const units = this.units;
		this.units = [];

		this.runner(units);
	}

	dispose(): void {
		this.units = [];

		super.dispose();
	}
E
Erich Gamma 已提交
739 740
}

741
export function nfcall(fn: Function, ...args: any[]): TPromise;
E
Erich Gamma 已提交
742 743
export function nfcall<T>(fn: Function, ...args: any[]): TPromise<T>;
export function nfcall(fn: Function, ...args: any[]): any {
744
	return new TPromise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result)), () => null);
E
Erich Gamma 已提交
745 746
}

747
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise;
E
Erich Gamma 已提交
748 749
export function ninvoke<T>(thisArg: any, fn: Function, ...args: any[]): TPromise<T>;
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any {
750
	return new TPromise((c, e) => fn.call(thisArg, ...args, (err: any, result: any) => err ? e(err) : c(result)), () => null);
751
}