提交 14b0ce99 编写于 作者: J Johannes Rieken

debt - async winjs.promise, add cancelation token

上级 0f851d0f
......@@ -10,14 +10,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape, DecorationData, DecorationRequest } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IDecorationsService, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
import { TPromise } from 'vs/base/common/winjs.base';
import { values } from 'vs/base/common/collections';
import { CancellationToken } from 'vs/base/common/cancellation';
class DecorationRequestsQueue {
private _idPool = 0;
private _requests: { [id: number]: DecorationRequest } = Object.create(null);
private _resolver: { [id: number]: Function } = Object.create(null);
private _resolver: { [id: number]: (data: DecorationData) => any } = Object.create(null);
private _timer: number;
......@@ -27,16 +27,18 @@ class DecorationRequestsQueue {
//
}
enqueue(handle: number, uri: URI): TPromise<DecorationData> {
enqueue(handle: number, uri: URI, token: CancellationToken): Promise<DecorationData> {
const id = ++this._idPool;
return new TPromise((resolve, reject) => {
const result = new Promise<DecorationData>(resolve => {
this._requests[id] = { id, handle, uri };
this._resolver[id] = resolve;
this._processQueue();
}, () => {
});
token.onCancellationRequested(() => {
delete this._requests[id];
delete this._resolver[id];
});
return result;
}
private _processQueue(): void {
......@@ -87,8 +89,8 @@ export class MainThreadDecorations implements MainThreadDecorationsShape {
const registration = this._decorationsService.registerDecorationsProvider({
label,
onDidChange: emitter.event,
provideDecorations: (uri) => {
return this._requestQueue.enqueue(handle, uri).then(data => {
provideDecorations: (uri, token) => {
return this._requestQueue.enqueue(handle, uri, token).then(data => {
if (!data) {
return undefined;
}
......
......@@ -9,7 +9,7 @@ import URI from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
export const IDecorationsService = createDecorator<IDecorationsService>('IFileDecorationsService');
......@@ -32,7 +32,7 @@ export interface IDecoration {
export interface IDecorationsProvider {
readonly label: string;
readonly onDidChange: Event<URI[]>;
provideDecorations(uri: URI): IDecorationData | TPromise<IDecorationData>;
provideDecorations(uri: URI, token: CancellationToken): IDecorationData | Thenable<IDecorationData>;
}
export interface IResourceDecorationChangeEvent {
......
......@@ -17,8 +17,8 @@ import { IdGenerator } from 'vs/base/common/idGenerator';
import { IIterator } from 'vs/base/common/iterator';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
class DecorationRule {
......@@ -180,7 +180,7 @@ class DecorationStyles {
let usedDecorations = new Set<string>();
for (let e = iter.next(); !e.done; e = iter.next()) {
e.value.data.forEach((value, key) => {
if (!isThenable<any>(value) && value) {
if (value && !(value instanceof DecorationDataRequest)) {
usedDecorations.add(DecorationRule.keyOf(value));
}
});
......@@ -229,9 +229,16 @@ class FileDecorationChangeEvent implements IResourceDecorationChangeEvent {
}
}
class DecorationDataRequest {
constructor(
readonly source: CancellationTokenSource,
readonly thenable: Thenable<void>,
) { }
}
class DecorationProviderWrapper {
readonly data = TernarySearchTree.forPaths<TPromise<void> | IDecorationData>();
readonly data = TernarySearchTree.forPaths<DecorationDataRequest | IDecorationData>();
private readonly _dispoable: IDisposable;
constructor(
......@@ -275,7 +282,7 @@ class DecorationProviderWrapper {
item = this._fetchData(uri);
}
if (item && !isThenable<void>(item)) {
if (item && !(item instanceof DecorationDataRequest)) {
// found something (which isn't pending anymore)
callback(item, false);
}
......@@ -285,7 +292,7 @@ class DecorationProviderWrapper {
const iter = this.data.findSuperstr(key);
if (iter) {
for (let item = iter.next(); !item.done; item = iter.next()) {
if (item.value && !isThenable<void>(item.value)) {
if (item.value && !(item.value instanceof DecorationDataRequest)) {
callback(item.value, true);
}
}
......@@ -297,27 +304,28 @@ class DecorationProviderWrapper {
// check for pending request and cancel it
const pendingRequest = this.data.get(uri.toString());
if (TPromise.is(pendingRequest)) {
pendingRequest.cancel();
if (pendingRequest instanceof DecorationDataRequest) {
pendingRequest.source.cancel();
this.data.delete(uri.toString());
}
const dataOrThenable = this._provider.provideDecorations(uri);
const source = new CancellationTokenSource();
const dataOrThenable = this._provider.provideDecorations(uri, source.token);
if (!isThenable(dataOrThenable)) {
// sync -> we have a result now
return this._keepItem(uri, dataOrThenable);
} else {
// async -> we have a result soon
const request = TPromise.wrap(dataOrThenable).then(data => {
const request = new DecorationDataRequest(source, Promise.resolve(dataOrThenable).then(data => {
if (this.data.get(uri.toString()) === request) {
this._keepItem(uri, data);
}
}, err => {
}).catch(err => {
if (!isPromiseCanceledError(err) && this.data.get(uri.toString()) === request) {
this.data.delete(uri.toString());
}
});
}));
this.data.set(uri.toString(), request);
return undefined;
......
......@@ -12,6 +12,7 @@ import URI from 'vs/base/common/uri';
import { Event, toPromise, Emitter } from 'vs/base/common/event';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
suite('DecorationsService', function () {
......@@ -34,7 +35,7 @@ suite('DecorationsService', function () {
readonly onDidChange: Event<URI[]> = Event.None;
provideDecorations(uri: URI) {
callCounter += 1;
return new TPromise<IDecorationData>(resolve => {
return new Promise<IDecorationData>(resolve => {
setTimeout(() => resolve({
color: 'someBlue',
tooltip: 'T'
......@@ -174,6 +175,7 @@ suite('DecorationsService', function () {
test('Decorations not showing up for second root folder #48502', async function () {
let cancelCount = 0;
let winjsCancelCount = 0;
let callCount = 0;
let provider = new class implements IDecorationsProvider {
......@@ -183,14 +185,19 @@ suite('DecorationsService', function () {
label: string = 'foo';
provideDecorations(uri: URI): TPromise<IDecorationData> {
provideDecorations(uri: URI, token: CancellationToken): TPromise<IDecorationData> {
token.onCancellationRequested(() => {
cancelCount += 1;
});
return new TPromise(resolve => {
callCount += 1;
setTimeout(() => {
resolve({ letter: 'foo' });
}, 10);
}, () => {
cancelCount += 1;
winjsCancelCount += 1;
});
}
};
......@@ -204,6 +211,7 @@ suite('DecorationsService', function () {
service.getDecoration(uri, false);
assert.equal(cancelCount, 1);
assert.equal(winjsCancelCount, 0);
assert.equal(callCount, 2);
reg.dispose();
......@@ -219,7 +227,7 @@ suite('DecorationsService', function () {
if (uri.path.match(/hello$/)) {
return { tooltip: 'FOO', weight: 17, bubble: true };
} else {
return new TPromise<IDecorationData>(_resolve => resolve = _resolve);
return new Promise<IDecorationData>(_resolve => resolve = _resolve);
}
}
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册