Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

// Because we test that the global error handler is called at various times.
setup({allow_uncaught_exception: true});
test(() => {
const source = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
});
const results = [];
source
.finally(() => {
results.push("finally called");
})
.subscribe({
next: (value) => results.push(value),
error: (e) => results.push(e.message),
complete: () => results.push("complete"),
});
assert_array_equals(results, [1, 2, 3, "finally called", "complete"],
"finally is called with teardown timing, before complete() is forwarded");
}, "finally(): Mirrors all values and completions from source");
test(() => {
const source = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.error(new Error("error from source"));
});
const results = [];
source
.finally(() => {
results.push("finally called");
})
.subscribe({
next: (value) => results.push(value),
error: (e) => results.push(e.message),
complete: () => results.push("complete"),
});
assert_array_equals(results, [1, 2, 3, "finally called", "error from source"],
"finally is called with teardown timing, before complete() is forwarded");
}, "finally(): Mirrors all values and errors from the source");
test(() => {
const results = [];
const source = new Observable((subscriber) => {
results.push("source subscribe");
subscriber.addTeardown(() => results.push("source teardown"));
results.push("source send complete");
subscriber.complete();
});
const result = source.finally(() => {
results.push("finally handler");
});
result.subscribe({
complete: () => results.push("result complete"),
});
assert_array_equals(results, [
"source subscribe",
"source send complete",
"source teardown",
"finally handler",
"result complete",
]);
}, "finally(): Callback handler fires BEFORE the source observable completes");
test(() => {
const results = [];
const source = new Observable((subscriber) => {
results.push("source subscribe");
subscriber.addTeardown(() => results.push("source teardown"));
results.push("source send error");
subscriber.error(new Error("error from source"));
});
const result = source.finally(() => {
results.push("finally handler");
});
result.subscribe({
error: (e) => results.push(e.message),
});
assert_array_equals(results, [
"source subscribe",
"source send error",
"source teardown",
"finally handler",
"error from source",
]);
}, "finally(): Callback handler fires BEFORE the source observable errors");
test(() => {
const results = [];
const source = new Observable((subscriber) => {
subscriber.complete();
});
const result = source
.finally(() => {
results.push("finally handler 1");
})
.finally(() => {
results.push("finally handler 2");
});
result.subscribe({ complete: () => results.push("result complete") });
assert_array_equals(results,
["finally handler 1", "finally handler 2", "result complete"]);
}, "finally(): Handlers run in composition order");
test(() => {
const source = new Observable(subscriber => {
subscriber.error("producer error");
});
const results = [];
self.addEventListener('error', e => results.push(e.error.message), {once: true});
source
.finally(() => {
throw new Error("error from finally");
})
.subscribe({
next: () => results.push("next"),
error: (e) => results.push(e),
complete: () => results.push("complete"),
});
assert_array_equals(results, ["error from finally", "producer error"]);
}, "finally(): Errors thrown in the finally handler " +
"(during Subscriber#error()) are reported to the global immediately");
test(() => {
const source = new Observable((subscriber) => {
subscriber.complete();
});
const results = [];
self.addEventListener('error', e => results.push(e.error.message), {once: true});
source
.finally(() => {
throw new Error("error from finally");
})
.subscribe({
next: () => results.push("next"),
error: (e) => results.push("unreached"),
complete: () => results.push("complete"),
});
assert_array_equals(results, ["error from finally", "complete"]);
}, "finally(): Errors thrown in the finally handler " +
"(during Subscriber#complete()) are reported to the global immediately");
test(() => {
const results = [];
const source = new Observable((subscriber) => {
subscriber.addTeardown(() => results.push("source teardown"));
});
const controller = new AbortController();
source
.finally(() => results.push("downstream finally handler"))
.subscribe({}, { signal: controller.signal });
controller.abort();
assert_array_equals(results, ["source teardown", "downstream finally handler"]);
}, "finally(): Callback is run if consumer aborts the subscription");
test(() => {
const results = [];
const result = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.complete();
}).flatMap((value) => {
results.push(`flatMap ${value}`);
return new Observable((subscriber) => {
subscriber.next(value);
subscriber.next(value);
subscriber.next(value);
subscriber.complete();
}).finally(() => {
results.push(`finally ${value}`);
});
});
result.subscribe({
next: (value) => results.push(`result ${value}`),
complete: () => results.push("result complete"),
});
assert_array_equals(results, [
"flatMap 1",
"result 1",
"result 1",
"result 1",
"finally 1",
"flatMap 2",
"result 2",
"result 2",
"result 2",
"finally 2",
"result complete",
]);
}, "finally(): Callback is run before next inner subscription in flatMap()");
test(() => {
const results = [];
const result = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.complete();
}).switchMap((value) => {
results.push(`switchMap ${value}`);
return new Observable((subscriber) => {
subscriber.next(value);
subscriber.next(value);
subscriber.next(value);
subscriber.complete();
}).finally(() => {
results.push(`finally ${value}`);
});
});
result.subscribe({
next: (value) => results.push(`result ${value}`),
complete: () => results.push("result complete"),
});
assert_array_equals(results, [
"switchMap 1",
"result 1",
"result 1",
"result 1",
"finally 1",
"switchMap 2",
"result 2",
"result 2",
"result 2",
"finally 2",
"result complete",
]);
}, "finally(): Callback is run before next inner subscription in switchMap()");