From 60af1936be964b88ce958dd481f149836f8590f7 Mon Sep 17 00:00:00 2001 From: Captain Caius <241342+captaincaius@users.noreply.github.com> Date: Sat, 13 Oct 2018 15:52:17 +0000 Subject: [PATCH] PoC for promises as first class async citizens with scheduled macrotasks until they're resolved --- lib/common/promise.ts | 56 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/lib/common/promise.ts b/lib/common/promise.ts index 876d964bb..b11d39462 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -5,6 +5,13 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ + +function scheduleMacroTaskWithCurrentZone( + source: string, callback: Function, data: TaskData, customSchedule: (task: Task) => void, + customCancel: (task: Task) => void): MacroTask { + return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel); +} + interface Promise { finally(onFinally?: () => U | PromiseLike): Promise; } @@ -94,6 +101,38 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr const REJECTED = false; const REJECTED_NO_CATCH = 0; + const PROMISECONSTRUCT_SOURCE = 'Promise.constructor'; + + interface PromiseTaskData extends TaskData { + promise: ZoneAwarePromise; + } + + function placeholderCallback() {} + + function scheduleTask(task: Task) { + return task; + } + + function clearTask(task: Task) { + } + + function makeTaskAwareResolver(promise: ZoneAwarePromise, state: boolean): (value: any) => void { + return (v) => { + try { + resolvePromise(promise, state, v); + } catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + const task: Task = (promise as any)['MACROTASK']; + if (task && task.state === 'scheduled') { + task.invoke(); + } else if(task) { + task.cancelScheduleRequest(); + } + }; + } + function makeResolver(promise: ZoneAwarePromise, state: boolean): (value: any) => void { return (v) => { try { @@ -339,10 +378,19 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr } (promise as any)[symbolState] = UNRESOLVED; (promise as any)[symbolValue] = []; // queue; - try { - executor && executor(makeResolver(promise, RESOLVED), makeResolver(promise, REJECTED)); - } catch (error) { - resolvePromise(promise, false, error); + + const options: PromiseTaskData = {promise: promise}; + + if(executor) { + const task = scheduleMacroTaskWithCurrentZone(PROMISECONSTRUCT_SOURCE, placeholderCallback, options, scheduleTask, clearTask); + (promise as any)['MACROTASK'] = task; + + try { + executor(makeTaskAwareResolver(promise, RESOLVED), makeTaskAwareResolver(promise, REJECTED)); + } catch (error) { + resolvePromise(promise, false, error); + task.cancelScheduleRequest(); + } } }