From e4dd8b646d689460a9f09bb0d023371e11283d3a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Oct 2017 15:28:06 +0200 Subject: [PATCH] add native promise polyfill, #35516 --- src/vs/base/common/winjs.polyfill.promise.ts | 77 +++++++++ .../common/winjs.polyfill.promise.test.ts | 152 ++++++++++++++++++ src/vs/editor/editor.main.ts | 7 + 3 files changed, 236 insertions(+) create mode 100644 src/vs/base/common/winjs.polyfill.promise.ts create mode 100644 src/vs/base/test/common/winjs.polyfill.promise.test.ts diff --git a/src/vs/base/common/winjs.polyfill.promise.ts b/src/vs/base/common/winjs.polyfill.promise.ts new file mode 100644 index 00000000000..65d46c7f076 --- /dev/null +++ b/src/vs/base/common/winjs.polyfill.promise.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Promise as WinJSPromise } from './winjs.base'; + +/** + * A polyfill for the native promises. The implementation is based on + * WinJS promises but tries to gap differences between winjs promises + * and native promises. + */ +export class PolyfillPromise implements Promise { + + static all(thenables: Thenable[]): PolyfillPromise { + return new PolyfillPromise(WinJSPromise.join(thenables).then(null, values => { + // WinJSPromise returns a sparse array whereas + // native promises return the *first* error + for (var key in values) { + if (values.hasOwnProperty(key)) { + return values[key]; + } + } + })); + } + + static race(thenables: Thenable[]): PolyfillPromise { + // WinJSPromise returns `{ key: , value: }` + // from the `any` call and Promise.race just wants the value + return new PolyfillPromise(WinJSPromise.any(thenables).then(entry => entry.value, err => err.value)); + } + + static resolve(value): PolyfillPromise { + return new PolyfillPromise(WinJSPromise.wrap(value)); + } + + static reject(value): PolyfillPromise { + return new PolyfillPromise(WinJSPromise.wrapError(value)); + } + + private _winjsPromise: WinJSPromise; + + constructor(winjsPromise: WinJSPromise); + constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any); + constructor(callback: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) { + + if (WinJSPromise.is(callback)) { + this._winjsPromise = callback; + } else { + this._winjsPromise = new WinJSPromise((resolve, reject) => { + let initializing = true; + callback(function (value) { + if (!initializing) { + resolve(value); + } else { + setImmediate(resolve, value); + } + }, function (err) { + if (!initializing) { + reject(err); + } else { + setImmediate(reject, err); + } + }); + initializing = false; + }); + } + } + + then(onFulfilled?: any, onRejected?: any): PolyfillPromise { + return new PolyfillPromise(this._winjsPromise.then(onFulfilled, onRejected)); + } + + catch(onRejected?: any): PolyfillPromise { + return new PolyfillPromise(this._winjsPromise.then(null, onRejected)); + } +} diff --git a/src/vs/base/test/common/winjs.polyfill.promise.test.ts b/src/vs/base/test/common/winjs.polyfill.promise.test.ts new file mode 100644 index 00000000000..a11908fe9ad --- /dev/null +++ b/src/vs/base/test/common/winjs.polyfill.promise.test.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; +import { Promise as WinJSPromise } from 'vs/base/common/winjs.base'; + +suite('Polyfill Promise', function () { + + test('sync-resolve, NativePromise', function () { + // native promise behaviour + const actual: string[] = []; + const promise = new Promise(resolve => { + actual.push('inCtor'); + resolve(null); + }).then(() => actual.push('inThen')); + actual.push('afterCtor'); + return promise.then(() => { + assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); + }); + }); + + test('sync-resolve, WinJSPromise', function () { + + // winjs promise behaviour + const actual: string[] = []; + const promise = new WinJSPromise(resolve => { + actual.push('inCtor'); + resolve(null); + }).then(() => actual.push('inThen')); + actual.push('afterCtor'); + return promise.then(() => { + assert.deepEqual(actual, ['inCtor', 'inThen', 'afterCtor']); + }); + }); + + test('sync-resolve, PolyfillPromise', function () { + + // winjs promise behaviour + const actual: string[] = []; + const promise = new PolyfillPromise(resolve => { + actual.push('inCtor'); + resolve(null); + }).then(() => actual.push('inThen')); + actual.push('afterCtor'); + return promise.then(() => { + assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); + }); + }); + + test('PolyfillPromise, executor has two params', function () { + return new PolyfillPromise(function () { + assert.equal(arguments.length, 2); + assert.equal(typeof arguments[0], 'function'); + assert.equal(typeof arguments[1], 'function'); + + arguments[0](); + }); + }); + + // run the same tests for the native and polyfill promise + ([Promise, PolyfillPromise]).forEach(PromiseCtor => { + + test(PromiseCtor.name + ', resolved value', function () { + return new PromiseCtor(resolve => resolve(1)).then(value => assert.equal(value, 1)); + }); + + test(PromiseCtor.name + ', rejected value', function () { + return new PromiseCtor((_, reject) => reject(1)).then(null, value => assert.equal(value, 1)); + }); + + test(PromiseCtor.name + ', catch', function () { + return new PromiseCtor((_, reject) => reject(1)).catch(value => assert.equal(value, 1)); + }); + + test(PromiseCtor.name + ', static-resolve', function () { + return PromiseCtor.resolve(42).then(value => assert.equal(value, 42)); + }); + + test(PromiseCtor.name + ', static-reject', function () { + return PromiseCtor.reject(42).then(null, value => assert.equal(value, 42)); + }); + + test(PromiseCtor.name + ', static-all, 1', function () { + return PromiseCtor.all([ + PromiseCtor.resolve(1), + PromiseCtor.resolve(2) + ]).then(values => { + assert.deepEqual(values, [1, 2]); + }); + }); + + test(PromiseCtor.name + ', static-all, 2', function () { + return PromiseCtor.all([ + PromiseCtor.resolve(1), + 3, + PromiseCtor.resolve(2) + ]).then(values => { + assert.deepEqual(values, [1, 3, 2]); + }); + }); + + test(PromiseCtor.name + ', static-all, 3', function () { + return PromiseCtor.all([ + PromiseCtor.resolve(1), + PromiseCtor.reject(13), + PromiseCtor.reject(12), + ]).catch(values => { + assert.deepEqual(values, 13); + }); + }); + + test(PromiseCtor.name + ', static-race, 1', function () { + return PromiseCtor.race([ + PromiseCtor.resolve(1), + PromiseCtor.resolve(2), + ]).then(value => { + assert.deepEqual(value, 1); + }); + }); + + test(PromiseCtor.name + ', static-race, 2', function () { + return PromiseCtor.race([ + PromiseCtor.reject(-1), + PromiseCtor.resolve(2), + ]).catch(value => { + assert.deepEqual(value, -1); + }); + }); + + test(PromiseCtor.name + ', static-race, 3', function () { + return PromiseCtor.race([ + PromiseCtor.resolve(1), + PromiseCtor.reject(2), + ]).then(value => { + assert.deepEqual(value, 1); + }); + }); + + test(PromiseCtor.name + ', throw in ctor', function () { + return new PromiseCtor(() => { + throw new Error('sooo bad'); + }).catch(err => { + assert.equal(err.message, 'sooo bad'); + }); + }); + + }); +}); diff --git a/src/vs/editor/editor.main.ts b/src/vs/editor/editor.main.ts index 2bb862cb86f..48005b9714e 100644 --- a/src/vs/editor/editor.main.ts +++ b/src/vs/editor/editor.main.ts @@ -19,6 +19,13 @@ import { createMonacoEditorAPI } from 'vs/editor/standalone/browser/standaloneEd import { createMonacoLanguagesAPI } from 'vs/editor/standalone/browser/standaloneLanguages'; import { EDITOR_DEFAULTS, WrappingIndent } from 'vs/editor/common/config/editorOptions'; +// When missing, polyfill the native promise +// with our winjs-based polyfill +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; +if (typeof global.Promise === 'undefined') { + global.Promise = PolyfillPromise; +} + // Set defaults for standalone editor (EDITOR_DEFAULTS).wrappingIndent = WrappingIndent.None; (EDITOR_DEFAULTS.contribInfo).folding = false; -- GitLab