RxJS

RxJS官網(wǎng)

1 概述

1.1 什么是 RxJS ?

RxJS 是一個(gè)用于處理異步編程的 JavaScript 庫骂际,目標(biāo)是使編寫異步和基于回調(diào)的代碼更容易

1.2 為什么要學(xué)習(xí) RxJS ?

就像 Angular 深度集成 TypeScript 一樣查近,Angular也深度集成了 RxJS

服務(wù)伟恶、表單阀捅、事件娩嚼、全局狀態(tài)管理体捏、異步請(qǐng)求 ...

1.3 快速入門

  1. 可觀察對(duì)象 ( Observable ) :類比 Promise 對(duì)象染苛,內(nèi)部可以用于執(zhí)行異步代碼,通過調(diào)用內(nèi)部提供的方法將異步代碼執(zhí)行的結(jié)果傳遞到可觀察對(duì)象外部

  2. 觀察者 ( Observer ):類比 then 方法中的回調(diào)函數(shù)毁兆,用于接收可觀察對(duì)象中傳遞出來數(shù)據(jù)

  3. 訂閱 ( subscribe ):類比 then 方法浙滤,通過訂閱將可觀察對(duì)象和觀察者連接起來,當(dāng)可觀察對(duì)象發(fā)出數(shù)據(jù)時(shí)气堕,訂閱者可以接收到數(shù)據(jù)纺腊。

    import { Observable } from "rxjs"
    
    const observable = new Observable(function (observer) {
      setTimeout(function () {
        observer.next({
          name: "張三"
        })
      }, 2000)
    })
    
    const observer = {
      next: function (value) {
        console.log(value)
      }
    }
    
    observable.subscribe(observer)
    

2. 可觀察對(duì)象

2.1 Observable

  1. Observable 對(duì)象內(nèi)部可以多次調(diào)用 next 方法向外發(fā)送數(shù)據(jù)

    const observable = new Observable(function (observer) {
      let index = 0
      setInterval(function () {
        observer.next(index++)
      }, 1000)
    })
    
    const observer = {
      next: function (value) {
        console.log(value)
      }
    }
    
    observable.subscribe(observer)
    
  2. 當(dāng)所有數(shù)據(jù)發(fā)送完成以后,可以調(diào)用 complete 方法終止數(shù)據(jù)發(fā)送

    const observable = new Observable(function (observer) {
      let index = 0
      let timer = setInterval(function () {
        observer.next(index++)
        if (index === 3) {
          observer.complete()
          clearInterval(timer)
        }
      }, 1000)
    })
    
    const observer = {
      next: function (value) {
        console.log(value)
      },
      complete: function () {
        console.log("數(shù)據(jù)發(fā)送完成")
      }
    }
    
    observable.subscribe(observer)
    
  3. 當(dāng) Observable 內(nèi)部邏輯發(fā)生錯(cuò)誤時(shí)茎芭,可以調(diào)用 error 方法將失敗信息發(fā)送給訂閱者揖膜,Observable 終止

    import { Observable } from "rxjs"
    
    const observable = new Observable(function (observer) {
      let index = 0
      let timer = setInterval(function () {
        observer.next(index++)
        if (index === 3) {
          observer.error("發(fā)生錯(cuò)誤")
          clearInterval(timer)
        }
      }, 1000)
    })
    
    const observer = {
      next: function (value) {
        console.log(value)
      },
      error: function (error) {
        console.log(error)
      }
    }
    
    observable.subscribe(observer)
    
  4. 可觀察對(duì)象是惰性的,只有被訂閱后才會(huì)執(zhí)行

    const observable = new Observable(function () {
      console.log("Hello RxJS")
    })
    // observable.subscribe()
    
  5. 可觀察對(duì)象可以有 n 多訂閱者梅桩,每次被訂閱時(shí)都會(huì)得到執(zhí)行

    const observable = new Observable(function () {
      console.log("Hello RxJS")
    })
    
    observable.subscribe()
    observable.subscribe()
    observable.subscribe()
    observable.subscribe()
    observable.subscribe()
    
  6. 取消訂閱

    import { interval } from "rxjs"
    
    const obs = interval(1000)
    const subscription = obs.subscribe(console.log)
    
    setTimeout(function () {
      subscription.unsubscribe()
    }, 2000)
    

2.2 Subject

  1. 用于創(chuàng)建空的可觀察對(duì)象壹粟,在訂閱后不會(huì)立即執(zhí)行,next 方法可以在可觀察對(duì)象外部調(diào)用

    import { Subject } from "rxjs"
    
    const demoSubject = new Subject()
    
    demoSubject.subscribe({next: function (value) {console.log(value)}})
    demoSubject.subscribe({next: function (value) {console.log(value)}})
    
    setTimeout(function () {
      demoSubject.next("hahaha")
    }, 3000)
    

2.3 BehaviorSubject

擁有 Subject 全部功能宿百,但是在創(chuàng)建 Obervable 對(duì)象時(shí)可以傳入默認(rèn)值趁仙,觀察者訂閱后可以直接拿到默認(rèn)值

import { BehaviorSubject } from "rxjs"

const demoBehavior = new BehaviorSubject("默認(rèn)值")
demoBehavior.subscribe({next: function (value) {console.log(value)}})
demoBehavior.next("Hello")

2.4 ReplaySubject

功能類似 Subject,但有新訂閱者時(shí)兩者處理方式不同垦页,Subject 不會(huì)廣播歷史結(jié)果雀费,而 ReplaySubject 會(huì)廣播所有歷史結(jié)果

import { ReplaySubject } from "rxjs"

const rSubject = new ReplaySubject()

rSubject.subscribe(value => {
  console.log(value)
})

rSubject.next("Hello 1")
rSubject.next("Hello 2")

setTimeout(function () {
  rSubject.subscribe({next: function (value) {console.log(value)}})
}, 3000)

3. 輔助方法

3.1 range

range(start, length),調(diào)用方法后返回 observable 對(duì)象外臂,被訂閱后會(huì)發(fā)出指定范圍的數(shù)值

import { range } from "rxjs"

range(0, 5).subscribe(n => console.log(n))

// 0
// 1
// 2
// 3
// 4

方法內(nèi)部并不是一次發(fā)出 length 個(gè)數(shù)值坐儿,而是發(fā)送了 length 次,每次發(fā)送一個(gè)數(shù)值宋光,就是說內(nèi)部調(diào)用了 lengthnext 方法

3.2 of

將參數(shù)列表作為數(shù)據(jù)流返回

of("a", "b", [], {}, true, 20).subscribe(v => console.log(v))

3.3 from

Array貌矿、PromiseIterator 轉(zhuǎn)換為 observable 對(duì)象

from(["a", "b", "c"]).subscribe(v => console.log(v))
// a
// b
// c
import { from } from "rxjs"

function p() {
  return new Promise(function (resolve) {
    resolve([100, 200])
  })
}

from(p()).subscribe(v => console.log(v))
// [100, 200]

3.4 interval罪佳、timer

Interval:每隔一段時(shí)間發(fā)出一個(gè)數(shù)值逛漫,數(shù)值遞增

import { interval } from "rxjs"

interval(1000).subscribe(n => console.log(n))

timer:間隔時(shí)間過去以后發(fā)出數(shù)值,行為終止赘艳,或間隔時(shí)間發(fā)出數(shù)值后酌毡,繼續(xù)按第二個(gè)參數(shù)的時(shí)間間隔繼續(xù)發(fā)出值

import { timer } from "rxjs"

timer(2000).subscribe(n => console.log(n))
timer(0, 1000).subscribe(n => console.log(n))

3.5 concat

合并數(shù)據(jù)流克握,先讓第一個(gè)數(shù)據(jù)流發(fā)出值,結(jié)束后再讓第二個(gè)數(shù)據(jù)流發(fā)出值枷踏,進(jìn)行整體合并

import { concat, range } from "rxjs"

concat(range(1, 5), range(6, 5)).subscribe(console.log)

3.6 merge

合并數(shù)據(jù)流菩暗,多個(gè)參數(shù)一起發(fā)出數(shù)據(jù)流,按照時(shí)間線進(jìn)行交叉合并

import { merge, fromEvent, interval } from "rxjs"

const clicks = fromEvent(document, "click")
const timer = interval(1000)

merge(clicks, timer).subscribe(console.log)

3.7 combineLatest

將兩個(gè) Obserable 中最新發(fā)出的數(shù)據(jù)流進(jìn)行組合成新的數(shù)據(jù)流旭蠕,以數(shù)組的形式發(fā)出停团。和當(dāng)前最新的進(jìn)行組合

import { combineLatest, timer } from "rxjs"

const firstTimer = timer(0, 1000) // emit 0, 1, 2... after every second, starting from now
const secondTimer = timer(500, 1000) // emit 0, 1, 2... after every second, starting 0,5s from now
combineLatest(firstTimer, secondTimer).subscribe(console.log)

// [0, 0] after 0.5s
// [1, 0] after 1s
// [1, 1] after 1.5s
// [2, 1] after 2s

3.8 zip

將多個(gè) Observable 中的數(shù)據(jù)流進(jìn)行組合。和將來最新的進(jìn)行組合

import { zip, of } from "rxjs"
import { map } from "rxjs/operators"

let age = of(27, 25, 29)
let name = of("Foo", "Bar", "Beer")
let isDev = of(true, true, false)

zip(name, age, isDev)
  .pipe(map(([name, age, isDev]) => ({ name, age, isDev })))
  .subscribe(console.log)

// { name: 'Foo', age: 27, isDev: true }
// { name: 'Bar', age: 25, isDev: true }
// { name: 'Beer', age: 29, isDev: false }

3.9 forkJoin

forkJoinRx 版本的 Promise.all()掏熬,即表示等到所有的 Observable 都完成后佑稠,才一次性返回值

import axios from "axios"
import { forkJoin, from } from "rxjs"

axios.interceptors.response.use(response => response.data)

forkJoin({
  goods: from(axios.get("http://localhost:3005/goods")),
  category: from(axios.get("http://localhost:3005/category"))
}).subscribe(console.log)

3.10 throwError

返回可觀察對(duì)象并向訂閱者拋出錯(cuò)誤

import { throwError } from "rxjs"

throwError("發(fā)生了未知錯(cuò)誤").subscribe({ error: console.log })

3.11 retry

如果 Observable 對(duì)象拋出錯(cuò)誤,則該輔助方法會(huì)重新訂閱 Observable 以獲取數(shù)據(jù)流旗芬,參數(shù)為重新訂閱次數(shù)

import { interval, of, throwError } from "rxjs"
import { mergeMap, retry } from "rxjs/operators"

interval(1000)
  .pipe(
    mergeMap(val => {
      if (val > 2) {
        return throwError("Error!")
      }
      return of(val)
    }),
    retry(2)
  )
  .subscribe({
    next: console.log,
    error: console.log
  })

3.12 race

接收并同時(shí)執(zhí)行多個(gè)可觀察對(duì)象舌胶,只將最快發(fā)出的數(shù)據(jù)流傳遞給訂閱者

import { race, timer } from "rxjs"
import { mapTo } from "rxjs/operators"

const obs1 = timer(1000).pipe(mapTo("fast one"))
const obs2 = timer(3000).pipe(mapTo("medium one"))
const obs3 = timer(5000).pipe(mapTo("slow one"))

race(obs3, obs1, obs2).subscribe(console.log)

3.13 fromEvent

將事件轉(zhuǎn)換為 Observable

import { fromEvent } from "rxjs"

const btn = document.getElementById("btn")
// 可以將 Observer 簡(jiǎn)寫成一個(gè)函數(shù),表示 next
fromEvent(btn, "click").subscribe(e => console.log(e))

4. 操作符

  1. 數(shù)據(jù)流:從可觀察對(duì)象內(nèi)部輸出的數(shù)據(jù)就是數(shù)據(jù)流疮丛,可觀察對(duì)象內(nèi)部可以向外部源源不斷的輸出數(shù)據(jù)
  2. 操作符:用于操作數(shù)據(jù)流幔嫂,可以對(duì)象數(shù)據(jù)流進(jìn)行轉(zhuǎn)換,過濾等操作

4.1 map这刷、mapTo

map:對(duì)數(shù)據(jù)流進(jìn)行轉(zhuǎn)換婉烟,基于原有值進(jìn)行轉(zhuǎn)換

import { interval } from "rxjs"
import { map } from "rxjs/operators"

interval(1000)
  .pipe(map(n => n * 2))
  .subscribe(n => console.log(n))

mapTo:對(duì)數(shù)據(jù)流進(jìn)行轉(zhuǎn)換,不關(guān)心原有值暇屋,可以直接傳入要轉(zhuǎn)換后的值

import { interval } from "rxjs"
import { mapTo } from "rxjs/operators"

interval(1000)
  .pipe(mapTo({ msg: "接收到了數(shù)據(jù)流" }))
  .subscribe(msg => console.log(msg))

4.2 filter

對(duì)數(shù)據(jù)流進(jìn)行過濾

import { range } from "rxjs"
import { filter } from "rxjs/operators"

range(1, 10)
  .pipe(filter(n => n % 2 === 0))
  .subscribe(even => console.log(even))

4.3 pluck

獲取數(shù)據(jù)流對(duì)象中的屬性值

import { interval } from "rxjs"
import { pluck, mapTo } from "rxjs/operators"

interval(1000)
  .pipe(
    mapTo({ name: "張三", a: { b: "c" } }), 
    pluck("a", "b")
    )
  .subscribe(n => console.log(n))

4.4 first

獲取數(shù)據(jù)流中的第一個(gè)值或者查找數(shù)據(jù)流中第一個(gè)符合條件的值,類似數(shù)組中的 find 方法洞辣。獲取到值以后終止行為

import { interval } from "rxjs"
import { first } from "rxjs/operators"

interval(1000)
  .pipe(first())
  .subscribe(n => console.log(n))

interval(1000)
  .pipe(first(n => n === 3))
  .subscribe(n => console.log(n))

4.5 startWith

創(chuàng)建一個(gè)新的 observable 對(duì)象并將參數(shù)值發(fā)送出去咐刨,然后再發(fā)送源 observable 對(duì)象發(fā)出的值

在異步編程中提供默認(rèn)值的時(shí)候非常有用

import { interval } from "rxjs"
import { map, startWith } from "rxjs/operators"

interval(1000)
  .pipe(
    map(n => n + 100),
    startWith(505)
  )
  .subscribe(n => console.log(n))
// 505
// 100
// 101
// 102
// ...

4.6 every

查看數(shù)據(jù)流中的每個(gè)值是否都符合條件,返回布爾值扬霜。類似數(shù)組中的 every 方法

import { range } from "rxjs"
import { every, map } from "rxjs/operators"

range(1, 9)
  .pipe(
    map(n => n * 2),
    every(n => n % 2 === 0)
  )
  .subscribe(b => console.log(b))

4.7 delay定鸟、delayWhen

delay:對(duì)上一環(huán)節(jié)的操作整體進(jìn)行延遲,只執(zhí)行一次

import { from } from "rxjs"
import { delay, map, tap } from "rxjs/operators"

from([1, 2, 3])
  .pipe(
    delay(1000),
    tap(n => console.log("已經(jīng)延遲 1s", n)),
    map(n => n * 2),
    delay(1000),
    tap(() => console.log("又延遲了 1s"))
  )
  .subscribe(console.log)

// tap 操作符不會(huì)對(duì)數(shù)據(jù)流造成影響, 它被用來執(zhí)行簡(jiǎn)單的副作用, 比如輸出, 但是復(fù)雜的副作用不要在這執(zhí)行, 比如 Ajax

delayWhen:對(duì)上一環(huán)節(jié)的操作進(jìn)行延遲著瓶,上一環(huán)節(jié)發(fā)出多少數(shù)據(jù)流联予,傳入的回調(diào)函數(shù)就會(huì)執(zhí)行多次

import { range, timer } from "rxjs"
import { delayWhen } from "rxjs/operators"

range(1, 10)
  .pipe(
    delayWhen(n => {
      console.log(n)
      return timer(n * 1000)
    })
  )
  .subscribe(console.log)

4.8 taketakeWhile材原、takeUtil

take:獲取數(shù)據(jù)流中的前幾個(gè)

import { range } from "rxjs"
import { take } from "rxjs/operators"

range(1, 10).pipe(take(5)).subscribe(console.log)

takeWhile:根據(jù)條件從數(shù)據(jù)源前面開始獲取

import { range } from "rxjs"
import { takeWhile } from "rxjs/operators"

range(1, 10)
  .pipe(takeWhile(n => n < 8))
  .subscribe(console.log)

takeUntil:接收可觀察對(duì)象沸久,當(dāng)可觀察對(duì)象發(fā)出值時(shí),終止主數(shù)據(jù)源

import { interval, timer } from "rxjs"
import { takeUntil } from "rxjs/operators"

interval(100)
  .pipe(takeUntil(timer(2000)))
  .subscribe(console.log)
// 結(jié)果少兩個(gè)數(shù)據(jù)流的原因:第一次和最后一次余蟹,都需要延遲 100 毫秒卷胯。

4.9 skipskipWhile威酒、skipUntil

skip:跳過前幾個(gè)數(shù)據(jù)流

import { range } from "rxjs"
import { skip } from "rxjs/operators"

range(1, 10).pipe(skip(5)).subscribe(console.log)

skipWhile:根據(jù)條件進(jìn)行數(shù)據(jù)流的跳過

import { range } from "rxjs"
import { skipWhile } from "rxjs/operators"

range(1, 10)
  .pipe(skipWhile(n => n < 5))
  .subscribe(console.log)

skipUntil:跳過數(shù)據(jù)源中前多少時(shí)間發(fā)出的數(shù)據(jù)流窑睁,發(fā)送從這個(gè)時(shí)間以后數(shù)據(jù)源中發(fā)送的數(shù)據(jù)流

import { timer, interval } from "rxjs"
import { skipUntil } from "rxjs/operators"

interval(100)
  .pipe(skipUntil(timer(2000)))
  .subscribe(console.log)

4.10 last

獲取數(shù)據(jù)流中的最后一個(gè)

import { range } from "rxjs"
import { last } from "rxjs/operators"

range(1, 10).pipe(last()).subscribe(console.log)

如果數(shù)據(jù)源不變成完成狀態(tài)挺峡,則沒有最后一個(gè)

import { interval } from "rxjs"
import { last, take } from "rxjs/operators"

interval(1000).pipe(take(5), last()).subscribe(console.log)

4.11 concatAllconcatMap

concatAll:有時(shí) Observable 發(fā)出的又是一個(gè) Obervable担钮,concatAll 的作用就是將新的可觀察對(duì)象和數(shù)據(jù)源進(jìn)行合并

import { fromEvent, interval } from "rxjs"
import { map, take, concatAll } from "rxjs/operators"

fromEvent(document, "click")
  .pipe(
    map(event => interval(1000).pipe(take(2))),
    concatAll()
  )
  .subscribe(console.log)
import { map, concatAll } from "rxjs/operators"
import { of, interval } from "rxjs"

interval(1000)
  .pipe(
    map(val => of(val + 10)),
    concatAll()
  )
  .subscribe(console.log)

concatMap:合并可觀察對(duì)象并處理其發(fā)出的數(shù)據(jù)流

4.12 reduce橱赠、scan

reduce:類似 JavaScript 數(shù)組中的 reduce,對(duì)數(shù)數(shù)據(jù)進(jìn)行累計(jì)操作箫津。reduce 會(huì)等待數(shù)據(jù)源中的數(shù)據(jù)流發(fā)送完成后再執(zhí)行病线,執(zhí)行時(shí) reduce 內(nèi)部遍歷每一個(gè)數(shù)據(jù)流進(jìn)行累計(jì)操作,操作完成得到結(jié)果將結(jié)果作為數(shù)據(jù)流發(fā)出

import { interval } from "rxjs"
import { take, reduce } from "rxjs/operators"

interval(500)
  .pipe(
    take(5),
    reduce((acc, value) => acc += value, 0)
  )
  .subscribe(v => console.log())

scan:類似 reduce鲤嫡,進(jìn)行累計(jì)操作送挑,但執(zhí)行時(shí)機(jī)不同,數(shù)據(jù)源每次發(fā)出數(shù)據(jù)流 scan 都會(huì)執(zhí)行暖眼。reduce 是發(fā)送出最終計(jì)算的結(jié)果惕耕,而 scan 是發(fā)出每次計(jì)算的結(jié)果

import { interval } from "rxjs"
import { take, scan } from "rxjs/operators"

interval(500)
  .pipe(
    take(5),
    scan((acc, value) => acc += value, 0)
  )
  .subscribe(v => console.log())

4.13 mergeAllmergeMap

mergeAll:交叉合并可觀察對(duì)象

import { fromEvent, interval } from "rxjs"
import { map, mergeAll } from "rxjs/operators"

fromEvent(document, "click")
  .pipe(
    map(() => interval(1000)),
    mergeAll()
  )
  .subscribe(console.log)

mergeMap:交叉合并可觀察對(duì)象以后對(duì)可觀察對(duì)象發(fā)出的數(shù)據(jù)流進(jìn)行轉(zhuǎn)換

import { of, interval } from "rxjs"
import { mergeMap, map } from "rxjs/operators"

of("a", "b", "c")
  .pipe(mergeMap(x => interval(1000).pipe(map(i => x + i))))
  .subscribe(x => console.log(x))

4.14 throttleTime

節(jié)流诫肠,可觀察對(duì)象高頻次向外部發(fā)出數(shù)據(jù)流司澎,通過 throttleTime 限制在規(guī)定時(shí)間內(nèi)每次只向訂閱者傳遞一次數(shù)據(jù)流

import { fromEvent } from "rxjs"
import { throttleTime } from "rxjs/operators"

fromEvent(document, "click")
  .pipe(throttleTime(2000))
  .subscribe(x => console.log(x))

4.15 debounceTime

防抖,觸發(fā)高頻事件栋豫,只響應(yīng)最后一次

import { fromEvent } from "rxjs"
import { debounceTime } from "rxjs/operators"

fromEvent(document, "click")
  .pipe(debounceTime(1000))
  .subscribe(x => console.log(x))

4.16 distinctUntilChanged

檢測(cè)數(shù)據(jù)源當(dāng)前發(fā)出的數(shù)據(jù)流是否和上次發(fā)出的相同挤安,如相同,跳過丧鸯,不相同蛤铜,發(fā)出

import { of } from "rxjs"
import { distinctUntilChanged } from "rxjs/operators"

of(1, 1, 2, 2, 2, 1, 1, 2, 3, 3, 4)
  .pipe(distinctUntilChanged())
  .subscribe(x => console.log(x)) // 1, 2, 1, 2, 3, 4

4.17 groupBy

對(duì)數(shù)據(jù)流進(jìn)行分組

import { of } from "rxjs"
import { mergeMap, groupBy, toArray } from "rxjs/operators"

of(
  { name: "Sue", age: 25 },
  { name: "Joe", age: 30 },
  { name: "Frank", age: 25 },
  { name: "Sarah", age: 35 }
)
  .pipe(
    groupBy(person => person.age),
    mergeMap(group => group.pipe(toArray()))
  )
  .subscribe(console.log)

// [{name: "Sue", age: 25}, { name: "Frank", age: 25 }]
// [{ name: "Joe", age: 30 }]
// [{ name: "Sarah", age: 35 }]

4.18 withLatestFrom

主數(shù)據(jù)源發(fā)出的數(shù)據(jù)流總是和支數(shù)據(jù)源中的最新數(shù)據(jù)流進(jìn)行結(jié)合,返回?cái)?shù)組

import { fromEvent, interval } from "rxjs"
import { withLatestFrom } from "rxjs/operators"

const clicks = fromEvent(document, "click")
const timer = interval(1000)
clicks.pipe(withLatestFrom(timer)).subscribe(console.log)

4.19 switchMap

切換可觀察對(duì)象

import { fromEvent, interval } from "rxjs"
import { switchMap } from "rxjs/operators"

fromEvent(document, "click")
  .pipe(switchMap(ev => interval(1000)))
  .subscribe(x => console.log(x))

5. 練習(xí)

5.1 元素拖拽

<style>
  #box {
    width: 200px;
    height: 200px;
    background: skyblue;
    position: absolute;
    left: 0;
    top: 0;
  }
</style>
<div id="box"></div>
// 原生 JavaScript
box.onmousedown = function (event) {
  let distanceX = event.clientX - event.target.offsetLeft
  let distanceY = event.clientY - event.target.offsetTop
  document.onmousemove = function (event) {
    let positionX = event.clientX - distanceX
    let positionY = event.clientY - distanceY
    box.style.left = positionX + "px"
    box.style.top = positionY + "px"
  }
  box.onmouseup = function () {
    document.onmousemove = null
  }
}
// RxJS
import { fromEvent } from "rxjs"
import { map, switchMap, takeUntil } from "rxjs/operators"

const box = document.getElementById("box")

fromEvent(box, "mousedown")
  .pipe(
    map(event => ({
      distanceX: event.clientX - event.target.offsetLeft,
      distanceY: event.clientY - event.target.offsetTop
    })),
    switchMap(({ distanceX, distanceY }) =>
      fromEvent(document, "mousemove").pipe(
        map(event => ({
          positionX: event.clientX - distanceX,
          positionY: event.clientY - distanceY
        })),
        takeUntil(fromEvent(document, "mouseup"))
      )
    )
  )
  .subscribe(({ positionX, positionY }) => {
    box.style.left = positionX + "px"
    box.style.top = positionY + "px"
  })

5.2 搜索

<input id="search" type="text" placeholder="請(qǐng)輸入搜索內(nèi)容..." />
import { fromEvent, from, throwError } from "rxjs"
import { debounceTime, distinctUntilChanged, map, switchMap, catchError } from "rxjs/operators"
import axios from "axios"

const search = document.getElementById("search")

fromEvent(search, "keyup")
  .pipe(
    debounceTime(700),
    map(event => event.target.value),
    distinctUntilChanged(),
    switchMap(keyword =>
      from(
        axios.get(`https://j1sonplaceholder.typicode.com/posts?q=${keyword}`)
      ).pipe(
        map(response => response.data),
        catchError(error => throwError(`發(fā)生了錯(cuò)誤: ${error.message}`))
      )
    )
  )
  .subscribe({
    next: value => {
      console.log(value)
    },
    error: error => {
      console.log(error)
    }
  })

5.3 串聯(lián)請(qǐng)求

先獲取token丛肢,再根據(jù)token獲取用戶信息

<button id="btn">獲取用戶信息</button>
import axios from "axios"
import { from, fromEvent } from "rxjs"
import { pluck, concatMap } from "rxjs/operators"

const button = document.getElementById("btn")

fromEvent(button, "click")
  .pipe(
    concatMap(event =>
      from(axios.get("http://localhost:3005/token")).pipe(
        pluck("data", "token")
      )
    ),
    concatMap(token =>
      from(axios.get("http://localhost:3005/userInfo")).pipe(
        pluck("data")
        )
    )
  )
  .subscribe(console.log)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末围肥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蜂怎,更是在濱河造成了極大的恐慌穆刻,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠步,死亡現(xiàn)場(chǎng)離奇詭異氢伟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幽歼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門朵锣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人试躏,你說我怎么就攤上這事猪勇。” “怎么了颠蕴?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵泣刹,是天一觀的道長(zhǎng)助析。 經(jīng)常有香客問我,道長(zhǎng)椅您,這世上最難降的妖魔是什么外冀? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮掀泳,結(jié)果婚禮上雪隧,老公的妹妹穿的比我還像新娘。我一直安慰自己员舵,他們只是感情好脑沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著马僻,像睡著了一般庄拇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上韭邓,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天措近,我揣著相機(jī)與錄音,去河邊找鬼女淑。 笑死瞭郑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸭你。 我是一名探鬼主播屈张,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼苇本!你這毒婦竟也來了袜茧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤瓣窄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后纳鼎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俺夕,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年贱鄙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劝贸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逗宁,死狀恐怖映九,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞎颗,我是刑警寧澤件甥,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布捌议,位于F島的核電站,受9級(jí)特大地震影響引有,放射性物質(zhì)發(fā)生泄漏瓣颅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一譬正、第九天 我趴在偏房一處隱蔽的房頂上張望宫补。 院中可真熱鬧,春花似錦曾我、人聲如沸粉怕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贫贝。三九已至,卻和暖如春虐秦,著一層夾襖步出監(jiān)牢的瞬間平酿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國打工悦陋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜈彼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓俺驶,卻偏偏與公主長(zhǎng)得像幸逆,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暮现,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容