You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
7.7 KiB
233 lines
7.7 KiB
'use strict'; |
|
// https://github.com/tc39/proposal-observable |
|
var $ = require('../internals/export'); |
|
var global = require('../internals/global'); |
|
var call = require('../internals/function-call'); |
|
var DESCRIPTORS = require('../internals/descriptors'); |
|
var setSpecies = require('../internals/set-species'); |
|
var aCallable = require('../internals/a-callable'); |
|
var isCallable = require('../internals/is-callable'); |
|
var isConstructor = require('../internals/is-constructor'); |
|
var anObject = require('../internals/an-object'); |
|
var isObject = require('../internals/is-object'); |
|
var anInstance = require('../internals/an-instance'); |
|
var defineProperty = require('../internals/object-define-property').f; |
|
var redefine = require('../internals/redefine'); |
|
var redefineAll = require('../internals/redefine-all'); |
|
var getIterator = require('../internals/get-iterator'); |
|
var getMethod = require('../internals/get-method'); |
|
var iterate = require('../internals/iterate'); |
|
var hostReportErrors = require('../internals/host-report-errors'); |
|
var wellKnownSymbol = require('../internals/well-known-symbol'); |
|
var InternalStateModule = require('../internals/internal-state'); |
|
|
|
var $$OBSERVABLE = wellKnownSymbol('observable'); |
|
var OBSERVABLE = 'Observable'; |
|
var SUBSCRIPTION = 'Subscription'; |
|
var SUBSCRIPTION_OBSERVER = 'SubscriptionObserver'; |
|
var getterFor = InternalStateModule.getterFor; |
|
var setInternalState = InternalStateModule.set; |
|
var getObservableInternalState = getterFor(OBSERVABLE); |
|
var getSubscriptionInternalState = getterFor(SUBSCRIPTION); |
|
var getSubscriptionObserverInternalState = getterFor(SUBSCRIPTION_OBSERVER); |
|
var Array = global.Array; |
|
var NativeObservable = global.Observable; |
|
var NativeObservablePrototype = NativeObservable && NativeObservable.prototype; |
|
|
|
var FORCED = !isCallable(NativeObservable) |
|
|| !isCallable(NativeObservable.from) |
|
|| !isCallable(NativeObservable.of) |
|
|| !isCallable(NativeObservablePrototype.subscribe) |
|
|| !isCallable(NativeObservablePrototype[$$OBSERVABLE]); |
|
|
|
var SubscriptionState = function (observer) { |
|
this.observer = anObject(observer); |
|
this.cleanup = undefined; |
|
this.subscriptionObserver = undefined; |
|
}; |
|
|
|
SubscriptionState.prototype = { |
|
type: SUBSCRIPTION, |
|
clean: function () { |
|
var cleanup = this.cleanup; |
|
if (cleanup) { |
|
this.cleanup = undefined; |
|
try { |
|
cleanup(); |
|
} catch (error) { |
|
hostReportErrors(error); |
|
} |
|
} |
|
}, |
|
close: function () { |
|
if (!DESCRIPTORS) { |
|
var subscription = this.facade; |
|
var subscriptionObserver = this.subscriptionObserver; |
|
subscription.closed = true; |
|
if (subscriptionObserver) subscriptionObserver.closed = true; |
|
} this.observer = undefined; |
|
}, |
|
isClosed: function () { |
|
return this.observer === undefined; |
|
} |
|
}; |
|
|
|
var Subscription = function (observer, subscriber) { |
|
var subscriptionState = setInternalState(this, new SubscriptionState(observer)); |
|
var start; |
|
if (!DESCRIPTORS) this.closed = false; |
|
try { |
|
if (start = getMethod(observer, 'start')) call(start, observer, this); |
|
} catch (error) { |
|
hostReportErrors(error); |
|
} |
|
if (subscriptionState.isClosed()) return; |
|
var subscriptionObserver = subscriptionState.subscriptionObserver = new SubscriptionObserver(subscriptionState); |
|
try { |
|
var cleanup = subscriber(subscriptionObserver); |
|
var subscription = cleanup; |
|
if (cleanup != null) subscriptionState.cleanup = isCallable(cleanup.unsubscribe) |
|
? function () { subscription.unsubscribe(); } |
|
: aCallable(cleanup); |
|
} catch (error) { |
|
subscriptionObserver.error(error); |
|
return; |
|
} if (subscriptionState.isClosed()) subscriptionState.clean(); |
|
}; |
|
|
|
Subscription.prototype = redefineAll({}, { |
|
unsubscribe: function unsubscribe() { |
|
var subscriptionState = getSubscriptionInternalState(this); |
|
if (!subscriptionState.isClosed()) { |
|
subscriptionState.close(); |
|
subscriptionState.clean(); |
|
} |
|
} |
|
}); |
|
|
|
if (DESCRIPTORS) defineProperty(Subscription.prototype, 'closed', { |
|
configurable: true, |
|
get: function () { |
|
return getSubscriptionInternalState(this).isClosed(); |
|
} |
|
}); |
|
|
|
var SubscriptionObserver = function (subscriptionState) { |
|
setInternalState(this, { |
|
type: SUBSCRIPTION_OBSERVER, |
|
subscriptionState: subscriptionState |
|
}); |
|
if (!DESCRIPTORS) this.closed = false; |
|
}; |
|
|
|
SubscriptionObserver.prototype = redefineAll({}, { |
|
next: function next(value) { |
|
var subscriptionState = getSubscriptionObserverInternalState(this).subscriptionState; |
|
if (!subscriptionState.isClosed()) { |
|
var observer = subscriptionState.observer; |
|
try { |
|
var nextMethod = getMethod(observer, 'next'); |
|
if (nextMethod) call(nextMethod, observer, value); |
|
} catch (error) { |
|
hostReportErrors(error); |
|
} |
|
} |
|
}, |
|
error: function error(value) { |
|
var subscriptionState = getSubscriptionObserverInternalState(this).subscriptionState; |
|
if (!subscriptionState.isClosed()) { |
|
var observer = subscriptionState.observer; |
|
subscriptionState.close(); |
|
try { |
|
var errorMethod = getMethod(observer, 'error'); |
|
if (errorMethod) call(errorMethod, observer, value); |
|
else hostReportErrors(value); |
|
} catch (err) { |
|
hostReportErrors(err); |
|
} subscriptionState.clean(); |
|
} |
|
}, |
|
complete: function complete() { |
|
var subscriptionState = getSubscriptionObserverInternalState(this).subscriptionState; |
|
if (!subscriptionState.isClosed()) { |
|
var observer = subscriptionState.observer; |
|
subscriptionState.close(); |
|
try { |
|
var completeMethod = getMethod(observer, 'complete'); |
|
if (completeMethod) call(completeMethod, observer); |
|
} catch (error) { |
|
hostReportErrors(error); |
|
} subscriptionState.clean(); |
|
} |
|
} |
|
}); |
|
|
|
if (DESCRIPTORS) defineProperty(SubscriptionObserver.prototype, 'closed', { |
|
configurable: true, |
|
get: function () { |
|
return getSubscriptionObserverInternalState(this).subscriptionState.isClosed(); |
|
} |
|
}); |
|
|
|
var $Observable = function Observable(subscriber) { |
|
anInstance(this, ObservablePrototype); |
|
setInternalState(this, { |
|
type: OBSERVABLE, |
|
subscriber: aCallable(subscriber) |
|
}); |
|
}; |
|
|
|
var ObservablePrototype = $Observable.prototype; |
|
|
|
redefineAll(ObservablePrototype, { |
|
subscribe: function subscribe(observer) { |
|
var length = arguments.length; |
|
return new Subscription(isCallable(observer) ? { |
|
next: observer, |
|
error: length > 1 ? arguments[1] : undefined, |
|
complete: length > 2 ? arguments[2] : undefined |
|
} : isObject(observer) ? observer : {}, getObservableInternalState(this).subscriber); |
|
} |
|
}); |
|
|
|
redefineAll($Observable, { |
|
from: function from(x) { |
|
var C = isConstructor(this) ? this : $Observable; |
|
var observableMethod = getMethod(anObject(x), $$OBSERVABLE); |
|
if (observableMethod) { |
|
var observable = anObject(call(observableMethod, x)); |
|
return observable.constructor === C ? observable : new C(function (observer) { |
|
return observable.subscribe(observer); |
|
}); |
|
} |
|
var iterator = getIterator(x); |
|
return new C(function (observer) { |
|
iterate(iterator, function (it, stop) { |
|
observer.next(it); |
|
if (observer.closed) return stop(); |
|
}, { IS_ITERATOR: true, INTERRUPTED: true }); |
|
observer.complete(); |
|
}); |
|
}, |
|
of: function of() { |
|
var C = isConstructor(this) ? this : $Observable; |
|
var length = arguments.length; |
|
var items = Array(length); |
|
var index = 0; |
|
while (index < length) items[index] = arguments[index++]; |
|
return new C(function (observer) { |
|
for (var i = 0; i < length; i++) { |
|
observer.next(items[i]); |
|
if (observer.closed) return; |
|
} observer.complete(); |
|
}); |
|
} |
|
}); |
|
|
|
redefine(ObservablePrototype, $$OBSERVABLE, function () { return this; }); |
|
|
|
$({ global: true, forced: FORCED }, { |
|
Observable: $Observable |
|
}); |
|
|
|
setSpecies(OBSERVABLE);
|
|
|