Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Spec fix #198

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 22 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,40 +99,39 @@ require("es-observable-tests").runTests(MyObservable);

An Observable represents a sequence of values which may be observed.

```js
interface Observable {

```ts
class Observable {
// Creates an observable from a callback
constructor(subscriber : SubscriberFunction);

// Subscribes to the sequence with an observer
subscribe(observer : Observer) : Subscription;

// Subscribes to the sequence with callbacks
subscribe(onNext : Function,
onError? : Function,
onComplete? : Function) : Subscription;
subscribe(onNext : (value) => void,
onError? : (errorValue) => void,
onComplete? : () => void) : Subscription;

// Returns itself
// Returns itself, but subclasses can override
[Symbol.observable]() : Observable;

// Converts items to an Observable
static of(...items) : Observable;

// Converts an observable or iterable to an Observable
static from(observable) : Observable;

}

interface Subscription {

// Cancels the subscription
unsubscribe() : void;

// A boolean value indicating whether the subscription is closed
get closed() : Boolean;
get closed() : boolean;
}

function SubscriberFunction(observer: SubscriptionObserver) : (void => void)|Subscription;
type SubscriberFunction =
(observer : SubscriptionObserver) => (() => void) | Subscription;
```

#### Observable.of ####
Expand All @@ -159,8 +158,8 @@ Observable.of("red", "green", "blue").subscribe({
`Observable.from` converts its argument to an Observable.

- If the argument has a `Symbol.observable` method, then it returns the result of
invoking that method. If the resulting object is not an instance of Observable,
then it is wrapped in an Observable which will delegate subscription.
invoking that method. If the resulting object is not a direct instance of Observable,
then it is wrapped into an Observable which will delegate subscription.
- Otherwise, the argument is assumed to be an iterable and the iteration values are
delivered synchronously when `subscribe` is called.

Expand Down Expand Up @@ -216,20 +215,19 @@ argument to **subscribe**.

All methods are optional.

```js
```ts
interface Observer {

// Receives the subscription object when `subscribe` is called
start(subscription : Subscription);
start(subscription : Subscription) : void;

// Receives the next value in the sequence
next(value);
next(value) : void;

// Receives the sequence error
error(errorValue);
error(errorValue) : void;

// Receives a completion notification
complete();
complete() : void;
}
```

Expand All @@ -238,19 +236,18 @@ interface Observer {
A SubscriptionObserver is a normalized Observer which wraps the observer object supplied to
**subscribe**.

```js
```ts
interface SubscriptionObserver {

// Sends the next value in the sequence
next(value);
next(value) : void;

// Sends the sequence error
error(errorValue);
error(errorValue) : void;

// Sends the completion notification
complete();
complete() : void;

// A boolean value indicating whether the subscription is closed
get closed() : Boolean;
get closed() : boolean;
}
```
59 changes: 35 additions & 24 deletions spec/constructor-properties.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ <h1>Observable.from ( _x_ )</h1>

<emu-alg>
1. Let _C_ be the *this* value.
1. If IsConstructor(C) is *false*, let _C_ be %Observable%.
1. If IsConstructor(_C_) is *false*, let _C_ be %Observable%.
1. Let _observableMethod_ be ? GetMethod(_x_, `@@observable`).
1. If _observableMethod_ is not *undefined*, then
1. Let _observable_ be ? Call(_observableMethod_, _x_, « »).
1. Let _observable_ be ? Call(_observableMethod_, _x_, « »).
1. If Type(_observable_) is not Object, throw a *TypeError* exception.
1. Let _constructor_ be ? Get(_observable_, `"constructor"`).
1. If SameValue(_constructor_, _C_) is *true*, return _observable_.
1. Let _subscriber_ be a new built-in function object as defined in Observable.from Delegating Functions.
1. Set _subscriber_'s [[Observable]] internal slot to _observable_.
1. Set _subscriber_.[[Observable]] to _observable_.
1. Else,
1. Let _iteratorMethod_ be ? GetMethod(_x_, `@@observable`).
1. Let _iteratorMethod_ be ? GetMethod(_x_, `@@iterator`).
1. If _iteratorMethod_ is *undefined*, throw a *TypeError* exception.
1. Let _subscriber_ be a new built-in function object as defined in Observable.from Iteration Functions.
1. Set _subscriber_'s [[Iterable]] internal slot to _x_.
1. Set _subscriber_'s [[IteratorMethod]] internal slot to _iteratorMethod_.
1. Set _subscriber_.[[Iterable]] to _x_.
1. Set _subscriber_.[[IteratorMethod]] to _iteratorMethod_.
1. Return Construct(_C_, « ‍_subscriber_ »).
</emu-alg>

Expand All @@ -39,7 +39,9 @@ <h1>Observable.from Delegating Functions</h1>
<p>When an Observable.from delegating function is called with argument _observer_, the following steps are taken:</p>

<emu-alg>
1. Let _observable_ be the value of the [[Observable]] internal slot of _F_.
1. Assert: _F_ has an [[Observable]] internal slot whose value is an Object.
1. If Type(_observer_) is not Object, throw a *TypeError* exception.
1. Let _observable_ be _F_.[[Observable]].
1. Return Invoke(_observable_, `"subscribe"`, « ‍_observer_ »).
</emu-alg>

Expand All @@ -49,24 +51,29 @@ <h1>Observable.from Delegating Functions</h1>
<emu-clause id="observable-from-iteration-functions">
<h1>Observable.from Iteration Functions</h1>

<p>An Observable.from iteration function is an anonymous built-in function that has [[Iterable]] and [[IteratorFunction]] internal slots.</p>
<p>An Observable.from iteration function is an anonymous built-in function that has [[Iterable]] and [[IteratorMethod]] internal slots.</p>

<p>When an Observable.from iteration function is called with argument _observer_, the following steps are taken:</p>

<emu-alg>
1. Let _iterable_ be the value of the [[Iterable]] internal slot of _F_.
1. Let _iteratorMethod_ be the value of the [[IteratorMethod]] internal slot of _F_.
1. Let _iterator_ be ? GetIterator(_items_, _iteratorMethod_).
1. Let _subscription_ be the value of _observer_'s [[Subscription]] internal slot.
1. Assert: _F_ has an [[Iterable]] internal slot whose value is an Object.
1. Assert: _F_ has an [[IteratorMethod]] internal slot whose value is an Object.
1. Assert: IsCallable(_F_.[[IteratorMethod]]) is *true*.
1. If Type(_observer_) is not Object, throw a *TypeError* exception.
1. Let _iterable_ be _F_.[[Iterable]].
1. Let _iteratorMethod_ be _F_.[[IteratorMethod]].
1. Let _iterator_ be ? GetIterator(_iterable_, ~sync~, _iteratorMethod_).
1. Repeat
1. Let _closed_ be ? ToBoolean(? GetV(_observer_, `"closed"`)).
1. If _closed_ is *true*, then
1. Perform ? IteratorClose(_iterator_, *undefined*).
1. Return *undefined*.
1. Let _next_ be ? IteratorStep(_iterator_).
1. If _next_ is *false*, then
1. Perform ! Invoke(_observer_, `"complete"`, « »).
1. Perform ? Invoke(_observer_, `"complete"`, « »).
1. Return *undefined*.
1. Let _nextValue_ be ? IteratorValue(_next_).
1. Perform ! Invoke(_observer_, `"next"`, « ‍_nextValue_ »).
1. If SubscriptionClosed(_subscription_) is *true*, then
1. Return ? IteratorClose(_iterator_, *undefined*).
1. Perform ? Invoke(_observer_, `"next"`, « ‍_nextValue_ »).
</emu-alg>

<p>The `length` property of an Observable.from iteration function is `1`.</p>
Expand All @@ -79,9 +86,10 @@ <h1>Observable.of ( ..._items_ )</h1>

<emu-alg>
1. Let _C_ be the *this* value.
1. If IsConstructor(C) is *false*, let _C_ be %Observable%.
1. If IsConstructor(_C_) is *false*, let _C_ be %Observable%.
1. Let _subscriber_ be a new built-in function object as defined in Observable.of Subscriber Functions.
1. Set _subscriber_'s [[Items]] internal slot to _items_.
1. Let _items_ be the List of arguments passed to this function.
1. Set _subscriber_.[[Items]] to _items_.
1. Return Construct(_C_, « ‍_subscriber_ »).
</emu-alg>

Expand All @@ -93,13 +101,16 @@ <h1>Observable.of Subscriber Functions</h1>
<p>When an Observable.of subscriber function is called with argument _observer_, the following steps are taken:</p>

<emu-alg>
1. Let _items_ be the value of the [[Items]] internal slot of _F_.
1. Let _subscription_ be the value of _observer_'s [[Subscription]] internal slot.
1. Assert: _F_ has an [[Items]] internal slot whose value is a List.
1. If Type(_observer_) is not Object, throw a *TypeError* exception.
1. Let _items_ be _F_.[[Items]].
1. For each element _value_ of _items_
1. Perform ! Invoke(_observer_, `"next"`, « ‍_value_ »).
1. If SubscriptionClosed(_subscription_) is *true*, then
1. Return *undefined*.
1. Perform ! Invoke(_observer_, `"complete"`, «‍ »).
1. Let _closed_ be ? ToBoolean(? GetV(_observer_, `"closed"`)).
1. If _closed_ is *true*, then return *undefined*.
1. Perform ? Invoke(_observer_, `"next"`, « ‍_value_ »).
1. Let _closed_ be ? ToBoolean(? GetV(_observer_, `"closed"`)).
1. If _closed_ is *true*, then return *undefined*.
1. Perform ? Invoke(_observer_, `"complete"`, « »).
1. Return *undefined*.
</emu-alg>

Expand Down
24 changes: 22 additions & 2 deletions spec/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@
contributors: Kevin Smith
</pre>

<emu-clause id="warn-if-abrupt" aoid="WarnIfAbrupt">
<h1>WarnIfAbrupt ( _completion_, _result_ )</h1>

<p>WarnIfAbrupt is similar to ReturnIfAbrupt, but instead of propagating an error, it returns a value. For example, algorithm steps that say or are otherwise equivalent to:</p>

<emu-alg>
1. WarnIfAbrupt(_argument_, _value_).
</emu-alg>

<p>mean the same thing as:</p>

<emu-alg>
1. If _argument_ is abrupt, then
1. Perform HostReportErrors(« _argument_.[[Value]] »).
1. Return _value_.
1. Else,
1. Let _argument_ be _argument_.[[Value]].
</emu-alg>
</emu-clause>

<emu-clause id="observable-constructor">
<h1>The Observable Constructor</h1>

Expand All @@ -28,8 +48,8 @@ <h1>Observable ( _subscriber_ )</h1>
<emu-alg>
1. If NewTarget is *undefined*, throw a *TypeError* exception.
1. If IsCallable(_subscriber_) is *false*, throw a *TypeError* exception.
1. Let _observable_ be ? OrdinaryCreateFromConstructor(NewTarget, %ObservablePrototype%, « [[Subscriber]] »).
1. Set _observable's_ [[Subscriber]] internal slot to _subscriber_.
1. Let _observable_ be ? OrdinaryCreateFromConstructor(NewTarget, %ObservablePrototype%, « [[Subscriber]] »).
1. Set _observable.[[Subscriber]] to _subscriber_.
1. Return _observable_.
</emu-alg>
</emu-clause>
Expand Down
42 changes: 23 additions & 19 deletions spec/prototype-properties.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h1>Observable.prototype.subscribe ( _observer_ )</h1>
<emu-alg>
1. Let _O_ be the *this* value.
1. If Type(_O_) is not Object, throw a *TypeError* exception.
1. If _O_ does not have an [[Subscriber]] internal slot, throw a *TypeError* exception.
1. If _O_ does not have all the internal slots of an Observable object, throw a *TypeError* exception.
1. If IsCallable(_observer_) is *true*, then
1. Let _len_ be the actual number of arguments passed to this function.
1. Let _args_ be the List of arguments passed to this function.
Expand All @@ -23,24 +23,21 @@ <h1>Observable.prototype.subscribe ( _observer_ )</h1>
1. Perform ! CreateDataProperty(_observer_, `"complete"`, _completeCallback_).
1. Else if Type(_observer_) is not Object, Let _observer_ be ObjectCreate(%ObjectPrototype%).
1. Let _subscription_ be ? CreateSubscription(_observer_).
1. Let _startMethodResult_ be GetMethod(_observer_, `"start"`).
1. If _startMethodResult_.[[Type]] is ~normal~, then
1. Let _start_ be _startMethodResult_.[[Value]].
1. If _start_ is not *undefined*, then
1. Let _result_ be Call(_start_, _observer_, « _subscription_ »).
1. If _result_ is an abrupt completion, perform HostReportErrors(« _result_.[[Value]] »).
1. If SubscriptionClosed(_subscription_) is *true*, then
1. Return _subscription_.
1. Else if _startMethodResult_.[[Type]] is ~throw~, then perform HostReportErrors(« _startMethodResult_.[[Value]] »).
1. If _result_ is an abrupt completion, perform HostReportErrors(« _result_.[[Value]] »).
1. Let _start_ be GetMethod(_observer_, `"start"`).
1. WarnIfAbrupt(_start_, _subscription_).
1. If _start_ is not *undefined*, then
1. Let _result_ be Call(_start_, _observer_, « _subscription_ »).
1. WarnIfAbrupt(_result_, _subscription_).
1. If SubscriptionClosed(_subscription_) is *true*, then
1. Return _subscription_.
1. Let _subscriptionObserver_ be ? CreateSubscriptionObserver(_subscription_).
1. Let _subscriber_ be the value of _O's_ [[Subscriber]] internal slot.
1. Let _subscriber_ be _O_.[[Subscriber]].
1. Assert: IsCallable(_subscriber_) is *true*.
1. Let _subscriberResult_ be ExecuteSubscriber(_subscriber_, _subscriptionObserver_).
1. If _subscriberResult_ is an abrupt completion, then
1. Perform ! Invoke(_subscriptionObserver_, `"error"`, « ‍_subscriberResult_.[[value]] »).
1. Perform ! EmitSubscriptionHook(_subscriptionObserver_, `"error"`, ‍_subscriberResult_.[[value]]).
1. Else,
1. Set the [[Cleanup]] internal slot of _subscription_ to _subscriberResult_.[[value]].
1. Set _subscription_.[[Cleanup]] to _subscriberResult_.[[value]].
1. If SubscriptionClosed(_subscription_) is *true*, then
1. Perform ! CleanupSubscription(_subscription_).
1. Return _subscription_.
Expand All @@ -57,10 +54,9 @@ <h1>ExecuteSubscriber ( _subscriber_, _observer_ )</h1>
1. Let _subscriberResult_ be ? Call(_subscriber_, *undefined*, _observer_).
1. If _subscriberResult_ is *null* or *undefined*, return *undefined*.
1. If IsCallable(_subscriberResult_) is *true*, return _subscriberResult_.
1. Let _result_ be ? GetMethod(_subscriberResult_, `"unsubscribe"`).
1. If _result_ is *undefined*, throw a *TypeError* exception.
1. If Type(_subscriberResult_) is not Object, throw a *TypeError* exception.
1. Let _cleanupFunction_ be a new built-in function object as defined in Subscription Cleanup Functions.
1. Set _cleanupFunction_'s [[Subscription]] internal slot to _subscriberResult_.
1. Set _cleanupFunction_.[[Subscription]] to _subscriberResult_.
1. Return _cleanupFunction_.
</emu-alg>
</emu-clause>
Expand All @@ -73,8 +69,8 @@ <h1>Subscription Cleanup Functions</h1>
<p>When a subscription cleanup function _F_ is called the following steps are taken:</p>

<emu-alg>
1. Assert: _F_ as a [[Subscription]] internal slot whose value is an Object.
1. Let _subscription_ be the value of _F_'s [[Subscription]] internal slot.
1. Assert: _F_ has a [[Subscription]] internal slot whose value is an Object.
1. Let _subscription_ be _F_.[[Subscription]].
1. Return Invoke(_subscription_, `"unsubscribe"`, « ‍»).
</emu-alg>

Expand All @@ -100,3 +96,11 @@ <h1>Observable.prototype [ @@observable ] ( )</h1>

<p>The value of the `name` property of this function is `"[Symbol.observable]"`.</p>
</emu-clause>

<emu-clause id="observable-prototype-@@toStringTag">
<h1>Observable.prototype [ @@toStringTag ] ( )</h1>

<p>The initial value of the `@@toStringTag` property of `Observable.prototype` is the String value `"Observable"`.</p>

<p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.</p>
</emu-clause>
Loading