UNPKG

5.12 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const observable_fns_1 = require("observable-fns");
4const doNothing = () => undefined;
5const returnInput = (input) => input;
6const runDeferred = (fn) => Promise.resolve().then(fn);
7function fail(error) {
8 throw error;
9}
10function isThenable(thing) {
11 return thing && typeof thing.then === "function";
12}
13/**
14 * Creates a hybrid, combining the APIs of an Observable and a Promise.
15 *
16 * It is used to proxy async process states when we are initially not sure
17 * if that async process will yield values once (-> Promise) or multiple
18 * times (-> Observable).
19 *
20 * Note that the observable promise inherits some of the observable's characteristics:
21 * The `init` function will be called *once for every time anyone subscribes to it*.
22 *
23 * If this is undesired, derive a hot observable from it using `makeHot()` and
24 * subscribe to that.
25 */
26class ObservablePromise extends observable_fns_1.Observable {
27 constructor(init) {
28 super(originalObserver => {
29 // tslint:disable-next-line no-this-assignment
30 const self = this;
31 const observer = Object.assign(Object.assign({}, originalObserver), { complete() {
32 originalObserver.complete();
33 self.onCompletion();
34 },
35 error(error) {
36 originalObserver.error(error);
37 self.onError(error);
38 },
39 next(value) {
40 originalObserver.next(value);
41 self.onNext(value);
42 } });
43 try {
44 this.initHasRun = true;
45 return init(observer);
46 }
47 catch (error) {
48 observer.error(error);
49 }
50 });
51 this.initHasRun = false;
52 this.fulfillmentCallbacks = [];
53 this.rejectionCallbacks = [];
54 this.firstValueSet = false;
55 this.state = "pending";
56 }
57 onNext(value) {
58 if (!this.firstValueSet) {
59 this.firstValue = value;
60 this.firstValueSet = true;
61 }
62 }
63 onError(error) {
64 this.state = "rejected";
65 this.rejection = error;
66 for (const onRejected of this.rejectionCallbacks) {
67 // Promisifying the call to turn errors into unhandled promise rejections
68 // instead of them failing sync and cancelling the iteration
69 runDeferred(() => onRejected(error));
70 }
71 }
72 onCompletion() {
73 this.state = "fulfilled";
74 for (const onFulfilled of this.fulfillmentCallbacks) {
75 // Promisifying the call to turn errors into unhandled promise rejections
76 // instead of them failing sync and cancelling the iteration
77 runDeferred(() => onFulfilled(this.firstValue));
78 }
79 }
80 then(onFulfilledRaw, onRejectedRaw) {
81 const onFulfilled = onFulfilledRaw || returnInput;
82 const onRejected = onRejectedRaw || fail;
83 let onRejectedCalled = false;
84 return new Promise((resolve, reject) => {
85 const rejectionCallback = (error) => {
86 if (onRejectedCalled)
87 return;
88 onRejectedCalled = true;
89 try {
90 resolve(onRejected(error));
91 }
92 catch (anotherError) {
93 reject(anotherError);
94 }
95 };
96 const fulfillmentCallback = (value) => {
97 try {
98 resolve(onFulfilled(value));
99 }
100 catch (error) {
101 rejectionCallback(error);
102 }
103 };
104 if (!this.initHasRun) {
105 this.subscribe({ error: rejectionCallback });
106 }
107 if (this.state === "fulfilled") {
108 return resolve(onFulfilled(this.firstValue));
109 }
110 if (this.state === "rejected") {
111 onRejectedCalled = true;
112 return resolve(onRejected(this.rejection));
113 }
114 this.fulfillmentCallbacks.push(fulfillmentCallback);
115 this.rejectionCallbacks.push(rejectionCallback);
116 });
117 }
118 catch(onRejected) {
119 return this.then(undefined, onRejected);
120 }
121 finally(onCompleted) {
122 const handler = onCompleted || doNothing;
123 return this.then((value) => {
124 handler();
125 return value;
126 }, () => handler());
127 }
128 static from(thing) {
129 if (isThenable(thing)) {
130 return new ObservablePromise(observer => {
131 const onFulfilled = (value) => {
132 observer.next(value);
133 observer.complete();
134 };
135 const onRejected = (error) => {
136 observer.error(error);
137 };
138 thing.then(onFulfilled, onRejected);
139 });
140 }
141 else {
142 return super.from(thing);
143 }
144 }
145}
146exports.ObservablePromise = ObservablePromise;