In the previous series, I sequence translated into a sequence, the topic I do not translate, I feel the translation of a little lost something. Many other places are likened and described by stream.
Visual observable
You have learned some of the most frequently used operators in RXJS programming. It is a bit abstract to discuss what a sequence operator feels. To help developers understand operators more easily, we use marble diagrams (pinball). Translation estimates are problematic) to standard visualization. They represent asynchronous data streams in an image, and you can find them in other RXJS data.
Let's look at the range operator, which returns a observable that emits integers within a specific range: as shown:
This long arrow represents the time of the observable,x axis representation, and each circle represents the value emitted when the OnNext () is called internally by observable. After the third value is generated, range invokes the oncompleted function represented by the vertical line.
Let's take a look at examples that contain several observable. The merge operator operates two different observable, and returns a new observable that merges the values.
- The interval operator returns a observable that generates an increase in the specified millisecond interval.
- In the following example, we will combine two different observable that use interval, and these two observable all produce values at different intervals:
var a =Rx.Observable.Interval $). Map ( function(i) {return ' A '+I;}); var B =Rx.Observable.Interval -). Map ( function(i) {return ' B '+I;});Rx.Observable.Merge (A, B). Subscribe ( function(x) {Console.Log(x);});
The results are as follows:
"B0, A0, B1, B2, A1, B3, B4 ...
- The merge operator is as follows:
Here, the dashed arrows along the y-axis point to the final result after each element in the A and B sequences changes. The C sequence represents the observable as a result, which contains the merged elements of A and B sequences. If the elements of a different sequence are emitted simultaneously, the sequence of elements of the merge sequence is random.
The underlying sequence (sequence) operator
- In almost dozens of operators of the RXJS transformation observable, there are also the most used collection-processing capabilities in any language, such as map, filter, and reduce. In Javascrpit, you can find these operators (functions) in an instance of the array.
- RXJS follows the conventions of JavaScript, you have the opportunity to find almost the same operator as the function, and in fact, we will show the real use of arrays and observable, and how similar the APIs are to be presented.
- Map
Map is the most used sequence conversion operator. It requires a observable and a function, and applies the function to each value of the source observable. It returns the observable of a new converted value.
In both cases, SRC will not change.
This code, and the following code, is defined using Logvalue:
var logvalue = function (val) {Console.log (val)};
He may be the one we pass to map to do some asynchronous computations that change that value. In some situations, map may not work as expected, and a better way to do this is to use the FLATMAP operator.
Filter
Filter requires a observable and a function, and it uses this function to test every element in the observable. It will return a sequence in which the element is the value that the function returns True.
Reduce
- Reduce (also do fold) needs a observable and returns a new observable that contains only a single item, the single item is the result of applying a function to each element. This function accepts the result of the current element and the last invocation of the function.
Reduce is a powerful operator that handles a sequence. It is actually a basic implementation of a complete subset called the aggregation operator (aggregate operators).
- Aggregate Operators
- The aggregation operator processes a sequence and returns a single value. For example: Rx.Observable.first requires a Observable and an optional assertion function and returns the first element that satisfies the assertion function.
- Calculating the average of a sequence is also an aggregation operation. RXJS provides an example of a average operation, but with this option, we use reduce to achieve this. Each aggregation operation can be implemented using only reduce.
var avg =Rx.Observable.Range0,5). Reduce ( function(prev, cur) {return{sum:prev.sum+ Cur,count:prev.Count +1};}, {sum:0, Count:0}). Map ( function(o) {return O.sum/O.Count;}); var subscription =avg.Subscribe function(x) {Console.Log' Average is: ', x);});
"Average Is:2
- The interpretation of the surface code, I will not translate, directly to see that can understand. Imagine that if we were to calculate the average speed of a walker now, it would be nice to use reduce, but suppose that, on the timeline, the Walker would never go down, then the aggregate operator like reduce would never call its observer (Obeserver) 's OnNext function.
- Happily, the RXJS team had already thought of this and provided us with a scan operator that played the role of reduce, but it sent each of the results. The code is as follows:
var avg =Rx.Observable.Interval +). Scan ( function (prev, cur) {return{sum:prev.sum+ Cur,count:prev.Count +1};}, {sum:0, Count:0}). Map ( function(o) {return O.sum/O.Count;}); var subscription =avg.Subscribe function (x) {Console.log (x);});
Using the above method, we can aggregate a sequence that consumes a long time or has no time limit. In the above code, we produce an incremental integer per second and replace the previous reduce with scan. We have achieved all the averages so far at intervals of every second.
- FlatMap
-If you have another observable it's the result of many nested observable will do? Most of the time, you think of unifying the elements of these nested observable into a single sequence. This is what flatmap to do clearly.
-The FLATMAP operator requires a observable parameter, and the element of this parameter is also observable, and returns the observable of the flat value of only one child observable.
We can see that each element in a (A1,A2,A3) is also a observable sequence, and once we apply the FLATMAP transformation function to a, we will get a observable that contains all the elements of all the different children of a.
- Flatmap is a powerful operator, but it is more difficult to understand than any of the other operators we have learned, and it is considered to be these observable concatall () functions.
- Contcatall is an array function that requires an array, and returns a flat, single array that contains the values of all the sub-arrays, not the sub-arrays themselves. We can use reduce to build a function like this:
function concatAll(source) {return source.reduce(function(a, b) {return a.concat(b);});}
We can use it this way:
concatAll([[0, 1, 2], [3, 4, 5], [6, 7, 8]]);// [012345678]
- Flatmap do the same thing, but not flat arrays but observable ...
- Canceling sequences
- In Rxjs, we can cancel a running observable. This is an advantage over the way the callback function or promise (some promise implementations support canceling) these asynchronous interactions.
- There are implicit or explicit ways to cancel the observable of the two.
1) Clear cancellation: Dispose
The observable itself does not have a cancellation method. When we subscribe to a observable, I get a disposable object that represents this particular subscription. This allows us to call the Dispose method in this object, and then this particular subscription will stop accepting notifications from observable.
In the following example, we subscribe to two counter observable,counter to emit an auto-increment integer per second. After two seconds we cancel the second subscription (subscription), and then we see that its output is stopped, but the output of the first subscriber is still in progress.
var counter =Rx.Observable.Interval +); var subscription1 =counter.Subscribe function(i) {Console.Log(' Subscription 1: ',I);}); var subscription2 =counter.Subscribe function(i) {Console.Log(' Subscription 2: ',I);}); SetTimeout ( function() {Console.Log(' Canceling subscription2! ');Subscription2.Dispose ();}, -);
The results will be as follows:
"Subscription 1:0
Subscription 2:0
Subscription 1:1
Subscription 2:1
Canceling subscription2!
Subscription 1:2
Subscription 1:3
Subscription 1:4
...
2) implicit cancellation via operator
Most of the time, the operator will automatically cancel the subscription for you. Like range or take, they will unsubscribe when the sequence ends or satisfies the conditions of an operation. More advanced operators, such as Withlastestfrom or Flatmaplastest, are automatically created and destroyed internally when they need to be subscribed when they dynamically create observable.
When we use the observable wrapper outside the API interface that does not provide cancellation functionality, when observable is canceled, observable will stop sending notifications, but the inside API will not be unregistered. For example, if you use a observable that wraps promise, observable will stop firing when canceled, but the promise inside will not stop.
- In the following code, we tried to unlink a subscription to a wrapper promise observable, and we also took promise as a traditional way of doing an operation. The promise will be executed in 5 seconds, but the unsubscribe will be executed immediately.
varnew Promise(function(resolve, reject) {5000);});p.then(function() {console.log(‘Potential side effect!‘);});var subscription = Rx.Observable.fromPromise(p).subscribe(function(msg) {console.log(‘Observable resolved!‘);});subscription.dispose();
After 5 seconds we will see:
"Potential side effect!
- If we cancel the subscription to observable, it will be very effective to stop the heavy observable accept notification. But this promise then method will continue, which shows us that canceling observable does not cancel the promise inside it.
- Therefore, it is important to know the details inside the observable using the external API interface. You can imagine that you have canceled a sequence, but some of its internal APIs are still running and have some side effects on your program that are really hard to capture.
Handling Errors
- In the callback function, we can use the traditional try/catch mechanism because it is synchronous. Because it runs before any asynchronous code, it will not catch any errors.
- The workaround in the callback function is to pass the error as a parameter to the callback function, which can be useful, but it will make the code quite fragile.
- Let's see what observable is like and catch errors:
- The OnError Handler
- When we talk about observer, we must remember three functions: OnNext, oncompleted, OnError. OnError is the key to effectively handling errors in observable.
- To show how it works, there will be a simple function that requires an array of JSON strings and returns a observable, which observable send the object after the JSON string is converted with Json.parse.
function getJSON(arr) {return Rx.Observable.from(arr).map(function(str) {varJSON.parse(str);return parsedJSON;});}
- By Getjson we will pass three JSON strings, the second string will contain a syntax error, so json.parse cannot parse it. We then subscribe to the result by providing OnNext and onerror processors:
getJSON([‘{"1": 1, "2": 2}‘,‘{"success: true}‘, // Invalid JSON string‘{"enabled": true}‘]).subscribe(function(json) {console.log(‘Parsed JSON: ‘, json);},function(err) {console.log(err.message);});
The results are as follows:
"Parsed JSON: {1:1, 2:2}
JSON.parse:unterminated string at line 1 Column 8 of the JSON data
- The first result of the array observable emits a JSON translation object, but the second throws an exception, and the OnError processor catches the exception and prints it. The default behavior is that whenever an exception occurs, observable will stop the launch and oncompleted will not be invoked.
- Catching exceptions
- So far we have known how to detect an anomaly and do something about it. But we haven't yet been able to respond to what we're going to do. The observable instance provides a catch operator that allows us to continue with other observable after responding to a observable error.
- The catch operator requires a observable or function that takes an exception as an argument, which returns another observable. In our case, because there is an error in the original observable, we want to observable launch a JSON object that contains the exception property.
function getjson(arr) {returnRx.Observable.from (arr). Map ( function(str) {varParsedjson = Json.parse (str);returnParsedjson;});}varCaught = Getjson ([' {' 1 ': 1, ' 2 ': 2} ',' {' 1:1} ']).Catch(rx.observable.return({error:' There is an error parsing JSON '}); Caught.subscribe ( function(JSON) {Console.log (' parsed JSON: ', JSON);},//Because We catch errors now, ' OnError ' won't be executed function(e) {Console.log (' ERROR ', e.message);});
- In the code above, we created a new observable called caught, which uses the catch operator to catch exceptions in the initial observable. If there is an exception, it will continue this sequence by using the observable that only emits an exception property of item to describe this error, the result is as follows:
"Parsed Json:object {1:1, 2:2}
Parsed Json:object {error: "There is an error parsing JSON"}
- Here is the catch operator's marble diagram (pinball):
- Notice that "X" is on the sequence and it represents an exception (error). Shapes of different observable values: triangles represent these values from another observable, where the observable is the observable we return in exceptional cases.
- Catch is very useful for interacting with exceptions in a sequence, and many of its behaviors are similar to traditional try-catch blocks. In many cases, it is convenient to ignore the exception of an item in the sequence and keep the sequence going. In these cases, we can use the retry operator.
- Retrying sequences
- There are times when there are just mistakes that don't require us to do anything. For example, a time-out for remote data requests due to sporadic Internet links or remote server outages, in which case it would be a good idea if we kept asking for the data we needed until it was successful. The retry did the following:
//this would try to retrieve The remote URL up to 5 times. Rx.dom. get ( '/products ' ). Retry (5 ). Subscribe (function (XHR) { Console.log (XHR); },function (ERR) { console.error (, err); });
- In the above code, we create a function that returns a observable that uses XMLHttpRequest to repeatedly fetch content to a URL. Since our links are likely to be unreliable, we have added retry (5) prior to the subscription to ensure that, in the event of an exception, the observable will be tried 5 times before it hangs and errors.
- When we use retry, there are two things that matter:
- 1) If we do not pass any parameters, it will be tried indefinitely until the observable ends and there is no exception. This is very dangerous for the program, once the observable has been an error. If we use multiple observable that are synchronized, it will have the same infinite loop result.
- 2) retry will re-retry the entire observable, even if some items are not abnormal. Because every time retry, it will rerun, it is also important that when we deal with certain items, it will lead to unexpected results.
RXJS Primer (3)----in-depth sequence