Skip to content

Instantly share code, notes, and snippets.

@JiaLiPassion
Last active August 30, 2021 14:27
Show Gist options
  • Save JiaLiPassion/a3229f77e47e6b6c4ab236a0c002b43a to your computer and use it in GitHub Desktop.
Save JiaLiPassion/a3229f77e47e6b6c4ab236a0c002b43a to your computer and use it in GitHub Desktop.
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("zone.js");
Zone.__load_patch("generator", (global, Zone, api) => {
const generator = function* () {};
const gen = generator();
const Generator = Object.getPrototypeOf(gen);
console.log(Generator.next);
if (!Generator[Zone.__symbol__("zonePatched")]) {
Generator[Zone.__symbol__("zonePatched")] = true;
api.patchMethod(Generator, "next", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.run(delegate, self, args, "native await");
});
api.patchMethod(Generator, "return", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.run(delegate, self, args, "native await");
});
api.patchMethod(Generator, "throw", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.runGuarded(delegate, self, args, "native await");
});
}
});
Zone.__load_patch("asyncGenerator", (global, Zone, api) => {
const generator = async function* () {};
const gen = generator();
const Generator = Object.getPrototypeOf(gen);
console.log(Generator.next);
if (!Generator[Zone.__symbol__("zonePatched")]) {
Generator[Zone.__symbol__("zonePatched")] = true;
api.patchMethod(Generator, "next", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.run(delegate, self, args, "native await");
});
api.patchMethod(Generator, "return", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.run(delegate, self, args, "native await");
});
api.patchMethod(Generator, "throw", (delegate) => (self, args) => {
const zone = self[Zone.__symbol__("zone")];
return !zone || zone === Zone.current
? delegate.apply(self, args)
: zone.runGuarded(delegate, self, args, "native await");
});
}
});
const createGenerator = function (generator, thisArg, args) {
const gen = generator.apply(thisArg, args);
gen[Zone.__symbol__("zone")] = Zone.current;
return gen;
};
const awaiter = async function (thisArg, _arguments, generator) {
const gen = createGenerator(generator, thisArg, _arguments);
let res = null;
while (!res || !res.done) {
try {
res = gen.next(res && (await res.value));
} catch (e) {
res = gen.throw(e);
}
}
return res?.value;
};
const forAwaiter = async function* (thisArg, _arguments, generator) {
const gen = createGenerator(generator, thisArg, _arguments);
let res = null;
while (!res || !res.done) {
try {
res = await gen.next(res?.value);
if (!res.done) {
yield res?.value;
}
} catch (e) {
res = await gen.throw(e);
if (!res.done) {
yield res?.value;
}
}
}
};
const logs = [];
const zone1 = Zone.current.fork({
name: "zone1",
onScheduleTask: (delegate, curr, target, task) => {
logs.push(`zone1 schedule task ${task.source}`);
return delegate.scheduleTask(target, task);
},
onInvokeTask: (delegate, curr, target, task, applyThis, applyArgs) => {
logs.push(`zone1 invoke task ${task.source}`);
return delegate.invokeTask(target, task, applyThis, applyArgs);
},
onInvoke: (
delegate,
curr,
target,
callback,
applyThis,
applyArgs,
source
) => {
logs.push(`zone1 invoke ${source}`);
return delegate.invoke(target, callback, applyThis, applyArgs, source);
},
onHandleError: (delegate, curr, target, error) => {
logs.push(`zone1 handle error ${error.message}`);
return true;
},
});
function compute(a) {
return a + 1;
}
function computeAsync(a, throwError = false) {
return new Promise((res, rej) =>
setTimeout(() => {
if (throwError) {
rej(new Error("throw error in promise"));
} else {
res(a + 1);
}
})
);
}
async function* asyncGenerators(a) {
console.log("zone", Zone.current.name, a);
a = await computeAsync(a);
yield a;
console.log("zone", Zone.current.name, a);
a = await computeAsync(a);
yield a;
console.log("zone", Zone.current.name, a);
}
function* asyncGeneratorsTransformed(a) {
yield computeAsync(a);
yield computeAsync(a);
yield computeAsync(a);
}
async function testAsync1(options = { a: 0, throwError: false }) {
let a = options.a;
a++;
console.log("before await", a, Zone.current.name);
a = await compute(a);
a++;
if (options.throwError) {
throw new Error("throw error in async function");
}
console.log("after first await", a, Zone.current.name);
a = await compute(a);
a++;
console.log("after second await", a, Zone.current.name);
return a;
}
const generatorTest1 = function* (options = { a: 0, throwError: false }) {
let a = options.a;
a++;
logs.push(`before await ${Zone.current.name}`);
a = yield compute(a);
logs.push(`after await1 ${Zone.current.name} ${a}`);
a++;
if (options.throwError) {
throw new Error("throw error in async function");
}
a = yield compute(a);
logs.push(`after await2 ${Zone.current.name} ${a}`);
return a;
};
async function testAsync2(options = { a: 0, throwError: false }) {
let a = options.a;
a++;
console.log("before await", a, Zone.current.name);
a = await computeAsync(a);
a++;
console.log("after first await", a, Zone.current.name);
a = await computeAsync(a, options.throwError);
a++;
console.log("after second await", a, Zone.current.name);
return a;
}
const generatorTest2 = function* (options = { a: 0, throwError: false }) {
let a = options.a;
a++;
logs.push(`before await ${Zone.current.name}`);
a = yield computeAsync(a);
logs.push(`after await1 ${Zone.current.name} ${a}`);
a++;
a = yield computeAsync(a, options.throwError);
logs.push(`after await2 ${Zone.current.name} ${a}`);
return Promise.resolve(a);
};
const generatorTest3 = function* (options = { a: 0, throwError: false }) {
let a = options.a;
a++;
if (options.throwError) {
throw new Error("testError in generatorTest3");
}
logs.push(`before await ${Zone.current.name}`);
a = yield computeAsync(a);
return a;
};
const generatorTest7 = function* (
options = {
a: 0,
throwError: false,
}
) {
let a = options.a;
a++;
logs.push(`before await ${Zone.current.name}`);
try {
a = yield computeAsync(a, options.throwError);
} catch (e1) {
console.log("caught first error: " + e1.message);
logs.push("caught first error: " + e1.message);
try {
a = yield computeAsync(a, options.throwError);
} catch (e2) {
logs.push("caught second error: " + e2.message);
}
}
logs.push(`after await ${Zone.current.name} ${a}`);
return a;
};
describe("native async/await", () => {
beforeEach(() => {
logs.length = 0;
});
afterEach(() => {
logs.length = 0;
});
describe("testAsync1", () => {
it("should run", (done) => {
zone1.run(() => {
expect(Zone.current.name).toEqual(zone1.name);
const r = awaiter(undefined, [], generatorTest1);
r.then((v) => {
expect(v).toEqual(4);
expect(Zone.current.name).toEqual(zone1.name);
done();
});
});
});
it("should run into the original zone after await without parameter", (done) => {
// Simulate code like this.
//
// zone1.run(async function (this: unknown) {
// expect(Zone.current.name).toEqual(zone1.name);
// const r = await testAsync1();
// expect(r).toEqual(4);
// expect(Zone.current.name).toEqual(zone1.name);
// expect(logs).toEqual([
// 'zone1 invoke undefined',
// 'before await zone1',
// 'zone1 invoke native await',
// 'after await1 zone1 2',
// 'zone1 invoke native await',
// 'after await2 zone1 4',
// 'zone1 invoke native await',
// ]);
// });
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(undefined, [], generatorTest1);
expect(r).toEqual(4);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 2",
"zone1 invoke native await",
"after await2 zone1 4",
"zone1 invoke native await",
]);
done();
});
});
});
it("should run into the original zone after await with parameter", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(undefined, [{ a: 1 }], generatorTest1);
expect(r).toEqual(5);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 invoke native await",
"after await2 zone1 5",
"zone1 invoke native await",
]);
done();
});
});
});
it("should trigger zone onHandleError", (done) => {
zone1.runGuarded(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
let r;
try {
r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest1
);
} catch (e) {
logs.push(e.message);
}
expect(r).toBeUndefined();
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 invoke native await",
"zone1 handle error throw error in async function",
"zone1 invoke native await",
]);
done();
});
});
});
});
describe("testAsync2", () => {
it("should run into the original zone with then instead of await", (done) => {
zone1.run(() => {
expect(Zone.current.name).toEqual(zone1.name);
const r = awaiter(undefined, [{ a: 1 }], generatorTest2);
r.then((v) => {
console.log("v", v);
expect(v).toEqual(5);
expect(Zone.current.name).toEqual(zone1.name);
done();
});
});
});
it("should run into the original zone after await without parameter", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(undefined, [], generatorTest2);
expect(r).toEqual(4);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 2",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await2 zone1 4",
"zone1 invoke native await",
]);
done();
});
});
});
it("should run into the original zone after await with parameter", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(undefined, [{ a: 1 }], generatorTest2);
expect(r).toEqual(5);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await2 zone1 5",
"zone1 invoke native await",
]);
done();
});
});
});
it("should trigger zone onHandleError", (done) => {
zone1.runGuarded(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest2
);
expect(r).toBeUndefined();
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"zone1 handle error throw error in promise",
"zone1 invoke native await",
]);
done();
});
});
});
});
describe("multiple await", () => {
it("should run into the original zone", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
let r = yield awaiter(undefined, [], generatorTest1);
expect(r).toEqual(4);
r = yield awaiter(undefined, [], generatorTest2);
expect(r).toEqual(4);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 2",
"zone1 invoke native await",
"after await2 zone1 4",
"zone1 invoke native await",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 2",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await2 zone1 4",
"zone1 invoke native await",
]);
done();
});
});
});
it("should run into the original zone after await with parameter", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
let r = yield awaiter(undefined, [{ a: 1 }], generatorTest1);
expect(r).toEqual(5);
r = yield awaiter(undefined, [{ a: 1 }], generatorTest2);
expect(r).toEqual(5);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 invoke native await",
"after await2 zone1 5",
"zone1 invoke native await",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await2 zone1 5",
"zone1 invoke native await",
]);
done();
});
});
});
it("should trigger zone onHandleError", (done) => {
zone1.runGuarded(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
let r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest1
);
expect(r).toBeUndefined();
r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest2
);
expect(r).toBeUndefined();
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 invoke native await",
"zone1 handle error throw error in async function",
"zone1 invoke native await",
"before await zone1",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 schedule task setTimeout",
"zone1 invoke task setTimeout",
"zone1 invoke native await",
"zone1 handle error throw error in promise",
"zone1 invoke native await",
]);
done();
});
});
});
});
describe("testAsync3", () => {
it("should run into the original zone after await with parameter", (done) => {
zone1.run(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest3
);
console.log("logs", logs);
expect(r).toEqual(2);
expect(Zone.current.name).toEqual(zone1.name);
expect(logs).toEqual([
"zone1 invoke undefined",
"before await zone1",
"zone1 invoke native await",
"after await1 zone1 3",
"zone1 invoke native await",
"after await2 zone1 5",
"zone1 invoke native await",
]);
done();
});
});
});
});
describe("testAsync7", () => {
it("should run into the original zone after await with parameter", (done) => {
zone1.runGuarded(function () {
awaiter(undefined, [], function* () {
expect(Zone.current.name).toEqual(zone1.name);
const r = yield awaiter(
undefined,
[{ a: 1, throwError: true }],
generatorTest7
);
console.log("logs", logs);
done();
});
});
});
});
describe("for await", () => {
fit("should run into the original zone for async generator", (done) => {
zone1.runGuarded(awaiter(undefined, [], function* () {
await 1;
console.log('normal await');
const awaiter = forAwaiter(undefined, [1], asyncGenerators);
for await (let value of awaiter) {
zone1.run(() => {
console.log(value, Zone.current.name);
});
}
done();
}));
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment