Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 1741e70

Browse files
committed
WIP(core): use asynchooks to resolve es2017 native async/await issue
1 parent 288f472 commit 1741e70

21 files changed

+488
-80
lines changed

gulpfile.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ gulp.task('compile-node', function(cb) {
8282
tsc('tsconfig-node.json', cb);
8383
});
8484

85+
gulp.task('compile-node-es2017', function(cb) {
86+
tsc('tsconfig-node.es2017.json', cb);
87+
});
88+
8589
gulp.task('compile-esm', function(cb) {
8690
tsc('tsconfig-esm.json', cb);
8791
});
@@ -310,6 +314,11 @@ gulp.task('build', [
310314
'build/closure.js'
311315
]);
312316

317+
gulp.task('test/node2017', ['compile-node-es2017'], function(cb) {
318+
var testAsyncPromise = require('./build/test/node_async').testAsyncPromise;
319+
testAsyncPromise();
320+
});
321+
313322
gulp.task('test/node', ['compile-node'], function(cb) {
314323
var JasmineRunner = require('jasmine');
315324
var jrunner = new JasmineRunner();

lib/browser/define-property.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const OBJECT = 'object';
2222
const UNDEFINED = 'undefined';
2323

2424
export function propertyPatch() {
25-
Object.defineProperty = function(obj, prop, desc) {
25+
Object.defineProperty = function(obj: any, prop: string, desc: any) {
2626
if (isUnconfigurable(obj, prop)) {
2727
throw new TypeError('Cannot assign to read only property \'' + prop + '\' of ' + obj);
2828
}
@@ -49,7 +49,7 @@ export function propertyPatch() {
4949
return _create(obj, proto);
5050
};
5151

52-
Object.getOwnPropertyDescriptor = function(obj, prop) {
52+
Object.getOwnPropertyDescriptor = function(obj: any, prop: string) {
5353
const desc = _getOwnPropertyDescriptor(obj, prop);
5454
if (isUnconfigurable(obj, prop)) {
5555
desc.configurable = false;

lib/common/promise.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
233233
const delegate = (promise as any)[symbolState] ?
234234
(typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution :
235235
(typeof onRejected === FUNCTION) ? onRejected : forwardRejection;
236-
zone.scheduleMicroTask(source, () => {
236+
237+
zone.scheduleMicroTask(source, () => {
237238
try {
238239
resolvePromise(
239240
chainPromise, true, zone.run(delegate, undefined, [(promise as any)[symbolValue]]));
@@ -244,12 +245,17 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
244245
}
245246

246247
const ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }';
248+
type PROMISE = 'Promise';
247249

248250
class ZoneAwarePromise<R> implements Promise<R> {
249251
static toString() {
250252
return ZONE_AWARE_PROMISE_TO_STRING;
251253
}
252254

255+
get[Symbol.toStringTag]() {
256+
return 'Promise' as PROMISE;
257+
}
258+
253259
static resolve<R>(value: R): Promise<R> {
254260
return resolvePromise(<ZoneAwarePromise<R>>new this(null), RESOLVED, value);
255261
}
@@ -409,9 +415,13 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
409415
}
410416

411417
Ctor.prototype.then = function(onResolve: any, onReject: any) {
418+
const zone = this.zone;
412419
const wrapped = new ZoneAwarePromise((resolve, reject) => {
413420
originalThen.call(this, resolve, reject);
414421
});
422+
if (zone) {
423+
(wrapped as any).zone = zone;
424+
}
415425
return wrapped.then(onResolve, onReject);
416426
};
417427
(Ctor as any)[symbolThenPatched] = true;

lib/node/async_hooks_promise.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* patch nodejs async operations (timer, promise, net...) with
11+
* nodejs async_hooks
12+
*/
13+
Zone.__load_patch('node_async_hooks_promise', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
14+
let async_hooks;
15+
try {
16+
async_hooks = require('async_hooks');
17+
} catch (err) {
18+
print(err.message);
19+
return;
20+
}
21+
22+
const PROMISE_PROVIDER = 'PROMISE';
23+
const noop = function() {};
24+
25+
function print(message: string) {
26+
(process as any)._rawDebug(message);
27+
}
28+
29+
function init(id: number, provider: string, triggerId: number, parentHandle: any) {
30+
if (provider === PROMISE_PROVIDER) {
31+
if (!parentHandle) {
32+
print('no parenthandle');
33+
return;
34+
}
35+
const promise = parentHandle.promise;
36+
const originalThen = promise.then;
37+
38+
promise.then = function(onResolve: any, onReject: any) {
39+
const zone = Zone.current;
40+
const wrapped = new Promise((resolve, reject) => {
41+
originalThen.call(this, resolve, reject);
42+
});
43+
if (zone) {
44+
(wrapped as any).zone = zone;
45+
}
46+
return wrapped.then(onResolve, onReject);
47+
};
48+
}
49+
}
50+
51+
function before(id: number) {
52+
//print('before ' + id);
53+
}
54+
55+
function after(id: number) {
56+
//print('after ' + id);
57+
}
58+
59+
function destroy(id: number) {
60+
//print('destroy ' + id);
61+
}
62+
63+
async_hooks.createHook({ init, before, after, destroy }).enable();
64+
});

lib/node/async_promise.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* patch nodejs async operations (timer, promise, net...) with
11+
* nodejs async_hooks
12+
*/
13+
Zone.__load_patch('node_async_hooks_promise', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
14+
let async_hooks;
15+
try {
16+
async_hooks = require('async_hooks');
17+
} catch (err) {
18+
print(err.message);
19+
return;
20+
}
21+
22+
const PROMISE_PROVIDER = 'PROMISE';
23+
const noop = function() {};
24+
25+
const idPromise: {[key: number]: any} = {};
26+
27+
function print(...args: string[]) {
28+
if (!args) {
29+
return;
30+
}
31+
(process as any)._rawDebug(args.join(' '));
32+
}
33+
34+
function init(id: number, provider: string, triggerId: number, parentHandle: any) {
35+
if (provider === PROMISE_PROVIDER) {
36+
if (!parentHandle) {
37+
print('no parenthandle');
38+
return;
39+
}
40+
const promise = parentHandle.promise;
41+
const originalThen = promise.then;
42+
43+
const zone = Zone.current;
44+
if (zone.name === 'promise') {
45+
print('init promise', id.toString());
46+
}
47+
if (!zone.parent) {
48+
print('root zone');
49+
return;
50+
}
51+
const currentAsyncContext: any = {};
52+
currentAsyncContext.id = id;
53+
currentAsyncContext.zone = zone;
54+
idPromise[id] = currentAsyncContext;
55+
promise.then = function(onResolve: any, onReject: any) {
56+
const wrapped = new Promise((resolve, reject) => {
57+
originalThen.call(this, resolve, reject);
58+
});
59+
if (zone) {
60+
(wrapped as any).zone = zone;
61+
}
62+
return zone.run(() => {
63+
return wrapped.then(onResolve, onReject);
64+
});
65+
};
66+
}
67+
}
68+
69+
function before(id: number) {
70+
const currentAsyncContext = idPromise[id];
71+
if (currentAsyncContext) {
72+
print('before ' + id, currentAsyncContext.zone.name);
73+
api.setAsyncContext(currentAsyncContext);
74+
}
75+
}
76+
77+
function after(id: number) {
78+
const currentAsyncContext = idPromise[id];
79+
if (currentAsyncContext) {
80+
print('after ' + id, currentAsyncContext.zone.name);
81+
idPromise[id] = null;
82+
api.setAsyncContext(null);
83+
}
84+
}
85+
86+
function destroy(id: number) {
87+
print('destroy ' + id);
88+
}
89+
90+
async_hooks.createHook({init, before, after, destroy}).enable();
91+
});

lib/node/node.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ Zone.__load_patch(
111111
});
112112
};
113113
}
114-
115114
});
116115

117116

lib/node/rollup-main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
import '../zone';
1010
import '../common/promise';
1111
import '../common/to-string';
12+
import './async_hooks_promise';
1213
import './node';

0 commit comments

Comments
 (0)