└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # RxJS Operators Cheatsheet 2 | 3 | ## Introduction 4 | 5 | > [RxJS](https://rxjs-dev.firebaseapp.com/) is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code. 6 | 7 | _Like a Promise on steroids_. It provides a _lot_ of operators to manipulate the data emitted from an Observable and its flow. 8 | 9 | This is a simple cheatsheet for not forgetting some essentials operators. 10 | 11 | ## Basic Operators 12 | 13 | ### from 14 | 15 | Turn an array, promise, or iterable into an observable. 16 | 17 | ```js 18 | import { from } from 'rxjs'; 19 | 20 | //emit array as a sequence of values 21 | // The output will be: 1,2,3,4,5 22 | from([1, 2, 3, 4, 5]) 23 | .subscribe(data => ...); 24 | ``` 25 | 26 | ### of 27 | 28 | Emit variable amount of values in a sequence and then emits a complete notification. 29 | 30 | ```js 31 | import { of } from 'rxjs'; 32 | 33 | // Emits any number of provided values in sequence 34 | // The output will be: 1,2,3,4,5 35 | of(1, 2, 3, 4, 5) 36 | .subscribe(data => ...); 37 | ``` 38 | 39 | ### pipe 40 | 41 | Allows executing operators on emitted values in the order they were defined. 42 | 43 | ```js 44 | import { of, pipe } from 'rxjs'; 45 | 46 | of(1,2,3,4) 47 | pipe( 48 | op1(), 49 | op2(), 50 | op3() 51 | ) 52 | .subscribe(data => ...) 53 | 54 | // The emitted values will be the result of op3(op2(op1(value))) 55 | ``` 56 | 57 | ### tap 58 | 59 | Receives a value, takes an action which won't affect the value and returns the same value. 60 | 61 | _Useful for side effects as logging and such_. 62 | 63 | ```js 64 | import { of, pipe } from 'rxjs'; 65 | import { tap } from 'rxjs/operators'; 66 | 67 | of(1,2,3,4) 68 | pipe(tap(value => console.log(`The value is ${value}`))) 69 | .subscribe(data => ...) 70 | 71 | // The emitted value will be 1,2,3,4 72 | ``` 73 | 74 | ## Transformation 75 | 76 | ### concatMap 77 | 78 | Maps each value to an Observable, then flattens all of these inner Observables using concatAll. 79 | 80 | _Just like concatAll but applying a `map` function to each value (which is an Observable)._ 81 | 82 | ```js 83 | import { pipe } from 'rxjs'; 84 | import { concatMap } from 'rxjs/operators'; 85 | 86 | const ids = [1,2,3,4]; 87 | const data = []; 88 | 89 | from(ids) 90 | .pipe( 91 | concatMap(id => this.http.get('apiurl/resource/' + id)) 92 | ) 93 | .subscribe(httpResponse => this.data.push(httpResponse)); 94 | ``` 95 | 96 | ### defaultIfEmpty 97 | 98 | Allows setting a default value to emit if none was emitted from the source. 99 | 100 | ```js 101 | import { of, pipe } from 'rxjs'; 102 | import { defaultIfEmpty } from 'rxjs/operators'; 103 | // We create an empty observable 104 | of() 105 | .pipe(defaultIfEmpty('Empty!')) 106 | .subscribe(data => ...) 107 | // This will emit 'Empty!' 108 | ``` 109 | 110 | ### map 111 | 112 | Applies a function to each emitted value. 113 | 114 | ```js 115 | import { of, pipe } from 'rxjs'; 116 | import { map } from 'rxjs/operators'; 117 | 118 | of(1,2,3,4) 119 | .pipe(map(value => value * 2)) 120 | .subscribe(data => ...) 121 | // This will emit 2,4,6,8 122 | ``` 123 | 124 | ### reduce 125 | 126 | Reduces the values based on a function and a seed into one reduced value. 127 | 128 | ```js 129 | import { of, pipe } from 'rxjs'; 130 | import { reduce } from 'rxjs/operators'; 131 | 132 | of(1,2,3,4) 133 | .pipe( 134 | reduce((acc, singleValue) => acc + singleValue, 0) 135 | ) 136 | .subscribe(data => ...) 137 | // This will emit the value 10 138 | ``` 139 | 140 | ### mergeMap (also called flatMap) 141 | 142 | Maps each value to an Observable, then flattens all of these inner Observables using mergeAll. 143 | 144 | _Just like concatMap but each subsciption is not sequential (does not wait for the previous one to complete)._ 145 | 146 | Graphical example: 147 | ``` 148 | --(1)--------------(3)-------(5)----------------|-> 149 | (10)--(10)--(10)-|-> 150 | 151 | [mergeMap(i => 10 * i -- 10 * i -- 10 * 1 -|)] 152 | 153 | --(10)--(10)--(10)-(30)--(30)(50)(30)(50)--(50)-|-> 154 | ``` 155 | 156 | ### forkJoin 157 | 158 | Given observables emits the last emitted value of each observables. 159 | 160 | ```js 161 | import { ajax } from 'rxjs/ajax'; 162 | import { forkJoin } from 'rxjs'; 163 | 164 | const sources = { 165 | google: ajax.getJSON('https://api.github.com/users/google'), 166 | microsoft: ajax.getJSON('https://api.github.com/users/microsoft'), 167 | users: ajax.getJSON('https://api.github.com/users') 168 | } 169 | 170 | /* 171 | when all observables complete, provide the last 172 | emitted value from each as dictionary 173 | */ 174 | forkJoin( 175 | // as of RxJS 6.5+ we can use a dictionary of sources 176 | sources 177 | ) 178 | .subscribe(console.log); 179 | 180 | // The value emitted will be { google: object, microsoft: object, users: array } 181 | ``` 182 | 183 | ## Error Handling 184 | 185 | ### catchError 186 | 187 | Allows to handle error in an observable sequence. 188 | 189 | ```js 190 | import { throwError, of } from 'rxjs'; 191 | import { catchError } from 'rxjs/operators'; 192 | //emit error 193 | const source = throwError('This is an error!'); 194 | //gracefully handle error, returning observable with error message 195 | const example = source.pipe(catchError(val => of(`I caught: ${val}`))); 196 | //output: 'I caught: This is an error' 197 | const subscribe = example.subscribe(val => console.log(val)); 198 | ``` 199 | 200 | ### throwIfEmpty 201 | 202 | If the source does not emit anything at completion, this operator will force that to be considered an error. 203 | 204 | ```js 205 | import { of, pipe } from 'rxjs'; 206 | import { throwIfEmpty } from 'rxjs/operators'; 207 | // We create an empty observable 208 | of() 209 | .pipe(throwIfEmpty) 210 | .subscribe( 211 | data => ..., 212 | // This would print the "no value" error message 213 | err => console.log(err.message) 214 | ) 215 | ``` 216 | 217 | ### retry 218 | 219 | Useful for things like HTTP requests that may fail. Allows us to re-subscribe and retry up to a number of times. 220 | 221 | ```js 222 | import { interval, of, throwError } from 'rxjs'; 223 | import { mergeMap, retry } from 'rxjs/operators'; 224 | 225 | //emit value every 1s 226 | const source = interval(1000); 227 | const example = source.pipe( 228 | mergeMap(val => val > 5 229 | ? throwError('Error!') 230 | : of(val) 231 | ), 232 | //retry 2 times on error 233 | retry(2) 234 | ); 235 | ``` 236 | 237 | ## Filtering to Multiple Elements 238 | 239 | ### skip 240 | 241 | Skips a number of elements from the beginning of the source. 242 | 243 | ```js 244 | import { of, pipe } from 'rxjs'; 245 | import { skip } from 'rxjs/operators'; 246 | 247 | of(1,2,3,4,5) 248 | // Skips the first 3 elements 249 | .pipe(skip(3)) 250 | .subscribe(data => ...); 251 | ``` 252 | 253 | ### skipWhile 254 | 255 | Skips elements from the beginning of the source while the condition resolves to `true`. Once the condition resolves to `false`, all the next values will be emitted. 256 | 257 | ```js 258 | import { of, pipe } from 'rxjs'; 259 | import { skipWhile } from 'rxjs/operators'; 260 | 261 | of(3,2,1,5,1,3) 262 | // Skips the first 3 elements 263 | .pipe(skipWhile(value => value < 4)) 264 | .subscribe(data => ...); 265 | ``` 266 | 267 | ### take 268 | 269 | Takes a specific number of elements from the beginning of the source. 270 | 271 | ```js 272 | import { of, pipe } from 'rxjs'; 273 | import { take } from 'rxjs/operators'; 274 | 275 | of(5,4,3,2,1) 276 | // Emits the first 2 elements 277 | .pipe(take(2)) 278 | .subscribe(data => ...); 279 | ``` 280 | 281 | ### distinct 282 | 283 | Allows us to eliminate duplicated elements from a source. When a function is provided, that function will be used for determining the duplication. 284 | 285 | ```js 286 | import { of, pipe } from 'rxjs'; 287 | import { distinct } from 'rxjs/operators'; 288 | 289 | of(1,1,2,2,3,3,4,4) 290 | .pipe(distinct()) 291 | .subscribe(data => ...); 292 | 293 | --- 294 | 295 | const values = [ 296 | { id:1 , value:0 }, 297 | { id:2 , value:1 }, 298 | { id:1 , value:2 }, 299 | ] 300 | 301 | of(values) 302 | .pipe(distinct(value => value.id)) 303 | .subscribe(data => ...); 304 | 305 | // The emitted values will be { id:1 , value:0 } and { id:2 , value:1 }. 306 | ``` 307 | 308 | ### distinctUntilChanged 309 | 310 | Drops the value if the previous emitted value is identical to the one being evaluated. 311 | 312 | ```js 313 | import { of, pipe } from 'rxjs'; 314 | import { distinctUntilChanged } from 'rxjs/operators'; 315 | 316 | of(1,1,2,1,3,3,2) 317 | // The emitted values will be 1,2,1,3,2 318 | .pipe(distinctUntilChanged()) 319 | .subscribe(data => ...); 320 | ``` 321 | 322 | ### filter 323 | 324 | Filters the values from the source based on a condition applied to each value. 325 | 326 | ```js 327 | import { of, pipe } from 'rxjs'; 328 | import { filter } from 'rxjs/operators'; 329 | 330 | of(1,2,3,4,5) 331 | // The emitted values will be 2, 4 332 | .pipe(filter(value => value % 2 == 0)) 333 | .subscribe(data => ...); 334 | ``` 335 | 336 | ## Filtering to Single Element 337 | 338 | ### first 339 | 340 | Emits the first value of the source and unsubscribes. 341 | 342 | ```js 343 | import { of, pipe } from 'rxjs'; 344 | import { first } from 'rxjs/operators'; 345 | 346 | of(1,2,3,4,5) 347 | .pipe(first()) // Only emits value 1 348 | .subscribe(data => ...); 349 | ``` 350 | 351 | ### elementAt 352 | 353 | Emits the element at the specified position. Throws _ArgumentOutOfRangeError_ if index < 0 or the Observable completes before reaching the index position. 354 | 355 | ```js 356 | import { of, pipe } from 'rxjs'; 357 | import { elementAt } from 'rxjs/operators'; 358 | 359 | of(1,2,3,4,5) 360 | .pipe(elementAt(2)) // Only emits value 3 361 | .subscribe(data => ...); 362 | ``` 363 | 364 | ### find 365 | 366 | Allow to find the first element the match the condition, emit it and then unsubscribe. 367 | 368 | ```js 369 | import { of, pipe } from 'rxjs'; 370 | import { find } from 'rxjs/operators'; 371 | 372 | of(1,2,3,4,2) 373 | .pipe(find(value => value == 2)) // Only emits one value 2 374 | .subscribe(data => ...); 375 | ``` 376 | 377 | ### single 378 | 379 | Just like `find` operator but emits an error if more than one value is found or none is emitted fomr the source. 380 | 381 | ```js 382 | import { of, pipe } from 'rxjs'; 383 | import { single } from 'rxjs/operators'; 384 | 385 | // Prints "2" 386 | of(1,2,3,4) 387 | .pipe(find(value => value == 2)) 388 | .subscribe( 389 | data => console.log(data), 390 | err => console.log('error') 391 | ); 392 | 393 | // Prints "undefined" 394 | of(1,3,4) 395 | .pipe(find(value => value == 2)) 396 | .subscribe( 397 | data => console.log(data), 398 | err => console.log('error') 399 | ); 400 | 401 | // Prints "error" 402 | of(1,2,3,4,2) 403 | .pipe(find(value => value == 2)) 404 | .subscribe( 405 | data => console.log(data), 406 | err => console.log('error') 407 | ); 408 | 409 | // Prints "error" 410 | of() 411 | .pipe(find(value => value == 2)) 412 | .subscribe( 413 | data => console.log(data), 414 | err => console.log('error') 415 | ); 416 | ``` 417 | 418 | ## Grouping Observables 419 | 420 | ### concatAll 421 | 422 | This operator subscribes to each inner Observable only after the previous one is completed. Then, it returns the result as a single Observable. 423 | 424 | _Useful if order matters._ 425 | 426 | ```js 427 | import { of, pipe } from 'rxjs'; 428 | import { concatAll } from 'rxjs/operators'; 429 | 430 | const abc = ['a', 'b', 'c']; 431 | const def = ['d', 'e', 'f']; 432 | 433 | // We build an Observable of Observables 434 | of(of(...abc), of(...def)) 435 | .pipe(concatAll()) 436 | .subscribe(data => ...); 437 | 438 | // This will emit the values from the first observable (abc) and, after it completes, the values from the second observable (def). 439 | ``` 440 | 441 | ## Grouping Values 442 | 443 | ### toArray 444 | 445 | Collects all the emitted values into an array which is returned only after the source completes. 446 | 447 | ```js 448 | import { interval } from 'rxjs'; 449 | import { toArray, take } from 'rxjs/operators'; 450 | 451 | // This will emit [0,1,2] 452 | interval(100) 453 | .pipe( 454 | take(3), 455 | toArray() 456 | ) 457 | .subscribe(data => ...); 458 | ``` 459 | 460 | ### groupBy 461 | 462 | Groups observables based on a value into one grouped observable. 463 | 464 | ```js 465 | import { from, pipe } from 'rxjs'; 466 | import { groupBy, mergeMap, toArray } from 'rxjs/operators'; 467 | 468 | const values = [ 469 | { id:0, value: 0 }, 470 | { id:1, value: 0 }, 471 | { id:2, value: 1 }, 472 | { id:3, value: 2 }, 473 | ]; 474 | 475 | from(values) 476 | .pipe( 477 | // Group by value field 478 | groupBy(data => data.value), 479 | // Turn each group Observable into arrays 480 | mergeMap(group => group.pipe(toArray())) 481 | ) 482 | .subscribe(data => ...); 483 | 484 | /* 485 | This will emit: 486 | [{ id:0, value: 0 }, { id:1, value: 0 }] 487 | [{ id:2, value: 1 }] 488 | [{ id:3, value: 2 }] 489 | */ 490 | ``` 491 | --------------------------------------------------------------------------------