From 4ea5a02fa70d11b18a635e1271628d1ef1d397d4 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Mon, 24 Sep 2018 13:31:19 +0900 Subject: [PATCH 1/4] feat(async): support native async/await --- lib/common/promise.ts | 10 ++++++++++ lib/zone.ts | 31 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/common/promise.ts b/lib/common/promise.ts index ce765a260..d9e6d3221 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -207,6 +207,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr api.scheduleMicroTask(); // to make sure that it is running } } + if ((promise as any)['__zone_symbol__isAsync'] === true) { + Zone.setAsyncFrame(); + } } } // Resolving an already resolved promise is a noop. @@ -450,9 +453,16 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr proto[symbolThen] = originalThen; Ctor.prototype.then = function(onResolve: any, onReject: any) { + let isNativePromise = false; + if (this && !(this instanceof ZoneAwarePromise)) { + isNativePromise = true; + } const wrapped = new ZoneAwarePromise((resolve, reject) => { originalThen.call(this, resolve, reject); }); + if (isNativePromise) { + (wrapped as any)['__zone_symbol__isAsync'] = true; + } return wrapped.then(onResolve, onReject); }; (Ctor as any)[symbolThenPatched] = true; diff --git a/lib/zone.ts b/lib/zone.ts index 4de4e077d..52b54d003 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -307,6 +307,9 @@ interface ZoneType { /** @internal */ __symbol__(name: string): string; + + /** @internal */ + setAsyncFrame(): void; } /** @internal */ @@ -661,6 +664,9 @@ const Zone: ZoneType = (function(global: any) { } } + const detectAsyncFunction = async function() {}; + const AsyncFunction = detectAsyncFunction.constructor; + class Zone implements AmbientZone { static __symbol__: (name: string) => string = __symbol__; @@ -691,6 +697,10 @@ const Zone: ZoneType = (function(global: any) { return _currentTask; } + static setAsyncFrame() { + _currentZoneFrame = _asyncZoneFrame!; + } + static __load_patch(name: string, fn: _PatchFn): void { if (patches.hasOwnProperty(name)) { throw Error('Already loaded patch: ' + name); @@ -761,9 +771,26 @@ const Zone: ZoneType = (function(global: any) { callback: (...args: any[]) => T, applyThis?: any, applyArgs?: any[], source?: string): T { _currentZoneFrame = {parent: _currentZoneFrame, zone: this}; try { + if (callback && callback.constructor === AsyncFunction) { + const r = this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); + if (r && typeof r.then === 'function') { + _asyncZoneFrame = _currentZoneFrame; + return r.then((result: any) => { + _currentZoneFrame = _asyncZoneFrame!.parent!; + _isAsyncSet = true; + _asyncZoneFrame = null; + return result; + }); + } + return r; + } return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); } finally { - _currentZoneFrame = _currentZoneFrame.parent!; + if (!_isAsyncSet) { + _currentZoneFrame = _currentZoneFrame.parent!; + } else { + _isAsyncSet = false; + } } } @@ -1353,6 +1380,8 @@ const Zone: ZoneType = (function(global: any) { }, }; let _currentZoneFrame: _ZoneFrame = {parent: null, zone: new Zone(null, null)}; + let _asyncZoneFrame: _ZoneFrame|null = null; + let _isAsyncSet = false; let _currentTask: Task|null = null; let _numberOfNestedTaskFrames = 0; From 72d5749e43bbe63a663e474dba445fe8c6abdfd1 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Mon, 24 Sep 2018 16:36:36 +0900 Subject: [PATCH 2/4] should check the asyncfunction's name --- lib/zone.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zone.ts b/lib/zone.ts index 52b54d003..4edfad99d 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -665,7 +665,7 @@ const Zone: ZoneType = (function(global: any) { } const detectAsyncFunction = async function() {}; - const AsyncFunction = detectAsyncFunction.constructor; + const AsyncFunction = (detectAsyncFunction.constructor as any).name === 'AsyncFunction' ? detectAsyncFunction.constructor : null; class Zone implements AmbientZone { static __symbol__: (name: string) => string = __symbol__; From f20fd0c726bd1cd92c1b9a00e8f427337333b4bc Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Mon, 24 Sep 2018 16:53:33 +0900 Subject: [PATCH 3/4] lint --- lib/zone.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/zone.ts b/lib/zone.ts index 4edfad99d..4ca3384f2 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -665,7 +665,9 @@ const Zone: ZoneType = (function(global: any) { } const detectAsyncFunction = async function() {}; - const AsyncFunction = (detectAsyncFunction.constructor as any).name === 'AsyncFunction' ? detectAsyncFunction.constructor : null; + const AsyncFunction = (detectAsyncFunction.constructor as any).name === 'AsyncFunction' ? + detectAsyncFunction.constructor : + null; class Zone implements AmbientZone { static __symbol__: (name: string) => string = __symbol__; From b309cfa5e2470e931c8c2f88bcba3e715bd3daac Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Tue, 2 Oct 2018 18:34:12 +0900 Subject: [PATCH 4/4] outside native promise don not need to setAsyncFrame --- lib/common/promise.ts | 2 +- lib/zone.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/common/promise.ts b/lib/common/promise.ts index d9e6d3221..7558349a1 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -207,7 +207,7 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr api.scheduleMicroTask(); // to make sure that it is running } } - if ((promise as any)['__zone_symbol__isAsync'] === true) { + if ((promise as any)['__zone_symbol__isAsync'] === true && (promise as any)['__zone_symbol__outsideAsync'] !== true) { Zone.setAsyncFrame(); } } diff --git a/lib/zone.ts b/lib/zone.ts index 4ca3384f2..656e2a7c7 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -776,6 +776,7 @@ const Zone: ZoneType = (function(global: any) { if (callback && callback.constructor === AsyncFunction) { const r = this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); if (r && typeof r.then === 'function') { + r['__zone_symbol__outsideAsync'] = true; _asyncZoneFrame = _currentZoneFrame; return r.then((result: any) => { _currentZoneFrame = _asyncZoneFrame!.parent!;