Notas de estudio angular 7: RxJ

I. Descripción general

RxJsEs una biblioteca especialmente diseñada para manejar operaciones asincrónicas JavaScript. RxJsProporciona una clase principal: Observable, varias clases clave: Observer, Schedulers, Subjects. Piense RxJsen ello como orientado a eventos Lodash.
RxJsLa biblioteca se basa en funciones puras, por lo que tiene características potentes y funciones estables.
RxJsLa biblioteca tiene un conjunto completo de operadores para controlar el flujo de eventos en objetos observables, que pueden monitorear y controlar el flujo de eventos en varias etapas.
RxJsHay varios conceptos clave en él:

  1. Observables ( Observable). Se puede comparar con Promiseun objeto dentro del cual se pueden realizar operaciones asincrónicas y, una vez completada la operación asincrónica, los resultados se transfieren al exterior del objeto observable.
  2. ObserverLa función de devolución de llamada en el método analógico observador ( ) thense utiliza para recibir datos pasados ​​por el objeto observable.
  3. Método de analogía de suscripción ( Subscribe) then, cuando el objeto observable emite datos, el suscriptor puede recibir los datos.

2. observables

  1. Método de creación: new Observable()Creado a través del constructor, recibiendo un método como parámetro. Este método tiene un parámetro de suscriptor, a través del cual next()se pueden enviar datos.
const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
})
  1. ObservableEl código del objeto es diferido y diferente del Promiseobjeto, se ejecuta inmediatamente cuando se crea, Observableel código del objeto solo se ejecutará cuando esté suscrito.
  2. ObservableLas suscripciones a objetos requieren observable.subscribe()llamadas a métodos, similares a function.call()las llamadas a funciones. observable.subscribe()El método acepta un objeto como parámetro y contiene una función de devolución de llamada después de que el objeto observable emite datos. Cuando Observableel objeto ejecuta el método, subscriber.next()se activará observer.nextla función y observer.nextla función puede recibir subscriber.next()los parámetros del método.
const observer = {
    
    
    next: (value: any) => {
    
    
        console.log(value);
    }
}
observable.subscribe(observer);
  1. ObservableEl método del objeto subscriber.next()se puede llamar varias veces y cada llamada será observer.nextmonitoreada.
  2. ObservableUn objeto se puede suscribir varias veces y el código que contiene se ejecutará cada vez que se suscriba.
    Por ejemplo, en el siguiente código, Observableel objeto está suscrito a dos observadores diferentes.
const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
    subscriber.next(2);
})
const observer1 = {
    
    
    next: (value: any) => {
    
    
        console.log("我是订阅者一号,接收到:"+value);
    }
}
const observer2 = {
    
    
    next: (value: any) => {
    
    
        console.log("我是订阅者二号,接收到:"+value);
    }
}
observable.subscribe(observer1);
observable.subscribe(observer2);

Salida de consola:
Insertar descripción de la imagen aquí

  1. observable.subscribe()El método se puede comparar con function.call(), que ejecutará la llamada a la función y obtendrá el valor de retorno de la ejecución de la función. Pero call()el método solo se puede usar returnuna vez y solo puede recibir el valor de retorno de un evento sincrónico, mientras que subscribe()el método se puede usar nextvarias veces y puede recibir el valor de retorno de todos los eventos sincrónicos y asincrónicos.
const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
    setTimeout(function () {
    
    
        subscriber.next(2);
    })
})
const observer1 = {
    
    
    next: (value: any) => {
    
    
        console.log(value);
    }
}
observable.subscribe(observer1);

Insertar descripción de la imagen aquí

  1. ObservableEl código en el objeto se ejecuta sincrónicamente.
const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
    setTimeout(function () {
    
    
        subscriber.next(2);
    })
})
const observer1 = {
    
    
    next: (value: any) => {
    
    
        console.log(value);
    }
}
console.log('just before subscribe');
observable.subscribe(observer1);
console.log('just after subscribe');

Insertar descripción de la imagen aquí

  1. ObservablesubscriberAdemás de nextlos métodos utilizados para enviar datos, hay dos métodos en el objeto : complete()el método se utiliza para decirle al observador que se envían los datos; error(errorMessage)el método se utiliza para decirle al observador que se ha producido un error interno. En consecuencia, además de los atributos, observerlos observadores también tienen atributos y propiedades, que apuntan a eventos y funciones de devolución de llamada de eventos, respectivamente. Los métodos no pueden pasar parámetros. Además, ya sea un método o un método, significa que la observación ha terminado, si se utiliza para enviar datos más tarde, el observador no puede observarlos.nextcompleteerrorcompleteerrorcompletesubscriber.complete()subscriber.error(errorMessage)subscriber.next()
const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
    subscriber.complete();
    subscriber.next(2);   // 不能输出了
})
const observer1 = {
    
    
    next: (value: any) => {
    
    
        console.log(value);
    },
    complete: () => {
    
    
        console.log('complete');
    }
}
observable.subscribe(observer1);

Insertar descripción de la imagen aquí

const observable = new Observable((subscriber) => {
    
    
    subscriber.next(1);
    subscriber.error('发生了一个致命的错误');
    subscriber.next(2);
})
const observer1 = {
    
    
    next: (value: any) => {
    
    
        console.log(value);
    },
    error: (error: any) => {
    
    
        console.log(error);
    }
}
observable.subscribe(observer1);

Insertar descripción de la imagen aquí

三、Asunto

ObservableEl código del objeto observable creado a través del constructor se ejecutará una vez cada vez que se suscriba un observador. El objeto observable creado al usarlo Subjectes un objeto observable vacío. Al suscribirse, no se ejecutará de inmediato. Debe llamar manualmente a este objeto observable para enviar datos. Los escenarios relacionados con la radiodifusión pueden utilizar este tipo de objeto observable. Puede suscribirse a este objeto observable en varios lugares y llamar al método después de obtener los datos next. En este momento, todos los suscriptores recibirán los datos al mismo tiempo. El objeto observable creado con Subjectél es de multidifusión, lo que permite pasar valores a múltiples observadores; el Observableobjeto observable creado con él es de unidifusión y cada suscriptor tiene Observableuna ejecución independiente.

 // 创建一个空的可观察对象
const subject = new Subject();
// 有多个观察者订阅这个可观察对象
subject.subscribe({
    
    
    next: (v) => console.log(`observerA: ${
      
      v}`)
});
subject.subscribe({
    
    
    next: (v) => console.log(`observerB: ${
      
      v}`)
});
// 异步执行next方法获取数据,数据会被发送给所有的观察者
setTimeout(() => subject.next(1), 1000);
setTimeout(() => subject.next(2), 2000);

Insertar descripción de la imagen aquí

四、ComportamientoAsunto

BehaviorSubjectEs Subjectuna variante, guarda los últimos datos enviados, es decir, el "valor actual". Cuando un observador se suscribe se obtienen los últimos datos enviados. BehaviorSubjectAdecuado para expresar "valor en el tiempo". BehaviorSubjectSe puede recibir un valor predeterminado. El primer observador lo ejecutará inmediatamente al suscribirse y recibirá este valor predeterminado.

const behaviorSubject = new BehaviorSubject('默认值');
// 订阅的时候会立即执行一次,输出:ObserverA默认值
behaviorSubject.subscribe(value => {
    
    
    console.log('ObserverA'+value)
})
// 执行并被ObserverA观察到,输出:ObserverA第一次改变值
behaviorSubject.next('第一次改变值')
// 执行,被ObserverA观察到,输出:ObserverA第二次改变值
behaviorSubject.next('第二次改变值')
// 又一个订阅,此时behaviorSubject保存的是最新发送出去的值:第二次改变值
// 虽然这个订阅在next执行之后,但是仍然可以获取behaviorSubject里面保存的最新的数据
// 输出:ObserverB第二次改变值
behaviorSubject.subscribe(value => {
    
    
    console.log('ObserverB'+value)
})

Insertar descripción de la imagen aquí

五、RepetirAsunto

ReplaySubjectGuarde todos los datos enviados. ReplaySubjectRegistre Observablemúltiples valores durante la ejecución y reprodúzcalos a un nuevo observador. Al crear ReplaySubject, puede especificar cuántos valores reproducir y proporcionar los valores más recientes al nuevo observador.

// 规定回放的次数为2.即当有新的订阅者的时候,
// 会查看历史发送数据,并把最近的2条数据发送给新的订阅者
const replaySubject = new ReplaySubject(2);
// 这个订阅者会接收到replaySubject的所有数据
replaySubject.subscribe(value => {
    
    
    console.log(`replaySubjectA: ${
      
      value}`)
})
replaySubject.next(1);
replaySubject.next(2);
replaySubject.next(3);
// 这个订阅者会接收之前的两条数据以及之后的所有数据
replaySubject.subscribe(value => {
    
    
    console.log(`replaySubjectB: ${
      
      value}`)
})

6. Operador

Los operadores son Observablemétodos de tipos, como .map(...), .filter(...), .merge(...), etc. Un operador es esencialmente una función pura (pure function). Los operadores son RxJsla parte más útil de la biblioteca. Los operadores se dividen en operadores de instancia y operadores estáticos. Los operadores de instancia son Observablemétodos de instancias y los operadores estáticos son Observablemétodos estáticos de clases. El operador estático más común es el operador de creación, que se utiliza para crear Observable对象.

1.de

ofSe utiliza para crear parámetros simples Observabley emitir los parámetros dados.

const observableOf = of(1, '第二个元素', {
    
    name: '第三个元素'});
observableOf.subscribe(
    value => console.log(value)
)

Insertar descripción de la imagen aquí

2.de

Crea uno a partir de una matriz, un objeto similar a una matriz, Promiseun objeto iterador o un objeto similar a un objeto .ObservableObservable

const arr = [1, 2, 3];
// from创建一个Observable对象,依次将数组中的元素发射出去
const observable1 = from(arr)
observable1.subscribe((value) => {
    
    
    console.log(`数字数组元素:${
      
      value}`)
})
const promiseArr = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
// 依次将数组中的promise对象发射出去
const observable2 = from(promiseArr)
observable2.subscribe((value) => {
    
    
    // value是每一个promise对象元素
    value.then((value) => {
    
    
        console.log(`Promise.then接收到的数据:${
      
      value}`)
    })
})

Insertar descripción de la imagen aquí
fromLos métodos pueden Promiseconvertir objetos en Observableobjetos. Los datos enviados por promiseel objeto serán recibidos por la función de devolución de llamada del observador.resolvenext

// from方法可以将Promise对象转换为Observable对象
const promise = new Promise(resolve => {
    
    
    setTimeout(() => {
    
    
        resolve('promise resolved')
    }, 1000)
})
const obs1 = from(promise)
obs1.subscribe(value => console.log(value))
3.desdeEvento

desdeEvento

público estático fromEvent (objetivo: EventTargetLike, eventName: cadena, opciones: EventListenerOptions, selector: SelectorMethodSignature): Observable

Parámetro uno: DOMelemento o NodeList
Parámetro dos: nombre del evento click, mousedownetc.
Parámetro tres: valor opcional, parámetros pasados ​​a la función de escucha de eventos
Parámetro cuatro: valor opcional, resultado del procesamiento de la función, parámetros de recepción de la función de procesamiento de eventos, se debe devolver un valor único

// 第三个参数是配置对象,once:true表示只执行一次
// 第四个参数是点击事件执行完毕的回调函数
const observable = fromEvent(document, 'click',{
    
    once:true},(event)=>{
    
    
    console.log(event)
    console.log('执行了');
});
observable.subscribe((event) => {
    
    
    // 如果fromEvent中传递了第四个参数,即点击事件执行完毕的回调函数,
    // 那么这里的回调函数不会接收到任何参数
    // 如果fromEvent中没有传递第四个参数,这里会接受到一个event对象
    console.log(event)
    console.log('document被点击');
})
4.mapa

La
función de mapa es similar al método de la matriz map.

// 返回一个操作方法,接受一个observable对象作为参数
// 对observable对象发出的每一个值进行处理,返回一个新的observable对象
const operatorFunction = map((v: number) => v * v);
const result = operatorFunction(of(1, 2, 3));
result.subscribe(x => console.log(x));

mapMétodo de simulación

// 模拟map方法
// 1.接受一个函数作为参数
// 2.返回值也是一个函数
// 3.返回的这个函数,需要接受一个observable对象作为参数,
// 4.返回值是一个新的observable对象,并且发出的数据是对observable参数发出的数据进行fn处理
function myMap(fn: (arg0: any) => void) {
    
    
    return function (observable: any) {
    
    
        return Observable.create((observer: any) => {
    
    
            observable.subscribe((value: any) => {
    
    
                observer.next(fn(value));
            })
        })
    }
}
5.forkUnirse

forkJoin
es similar a Promise.allun método. Suponiendo que hay varios Observableobjetos que envían datos, forkJoinpuede esperar a que todos Observablelos objetos envíen datos y devuelvan un Observableobjeto. Los métodos asincrónicos mediante forkJoincomposición se ejecutan en paralelo.

// 模拟axios请求
const getUsername =function (){
    
    
    return new Promise(resolve => {
    
    
        setTimeout(() => {
    
    
            resolve('张三')
        }, 1000)
    })
}

const getAge =function (){
    
    
    return new Promise(resolve => {
    
    
        setTimeout(() => {
    
    
            resolve(78)
        }, 2000)
    })
}
// forkJoin()接受一个对象,对象指向的是Observable对象发出的数据
// forkJoin()返回一个Observable对象。
// subscribe里面直接传递回调函数就表示对发出的数据执行这个函数
forkJoin({
    
    
    username: from(getUsername()),
    age: from(getAge())
}).subscribe(console.log)
6.arrancar

pluck
Si Observablelos datos emitidos por el objeto son un objeto, puede usarlo pluckpara obtener las propiedades correspondientes. Acepta una cadena como parámetro, recorre los datos emitidos y obtiene los atributos correspondientes.

const observable = new Observable(subscriber => {
    
    
    subscriber.next({
    
    name: 'Brian'})
    subscriber.next({
    
    name: 'Joe'})
    subscriber.next({
    
    name: 'Sue'})
})
const pluckFn = pluck('name')
pluckFn(observable).subscribe(console.log)

Insertar descripción de la imagen aquí

7.intervalo

Envíe una cantidad incremental de datos cada período de tiempo. Acepta un intervalo de tiempo como argumento.

interval(1000).subscribe((value) => {
    
    
    console.log(value);
})

Enviar datos cada 1000ms

Insertar descripción de la imagen aquí

8.tubería()

En RxJSla biblioteca, pipees un método de programación funcional que se utiliza para unir varios operadores para transformar el Observable flujo de datos. Debido a que RxJShay muchas transformaciones de datos y operadores presentes en , y anidar estas operaciones directamente dificultaría la lectura y el mantenimiento del código, puede usar pipe métodos para combinar operadores en una canalización. Similar a lodashla combinación de funciones en, el siguiente código equivale a of(1,2,3)ejecutar mapel procesamiento primero, luego ejecutar filterel procesamiento y finalmente devolver uno nuevo procesado Observable.

of(1,2,3)
    .pipe(
        map(x => x * 2),
        filter(x => x > 3)
    ).subscribe(console.log)
9.cambiarMapa

switchMap
emite el resultado de aplicar la función de proyección a cada elemento emitido Observablepor la fuente y solo recibe el último valor interno proyectado.ObservableObservable

let observable = new Observable(subscriber => {
    
    
    subscriber.next(1);
    subscriber.next(2);
    subscriber.next(3);
    setTimeout(() => {
    
    
        subscriber.next(4);
        subscriber.complete();
    }, 1000);
})

const newObservable = observable.pipe(switchMap(value => {
    
    
    return of(value + '被switchMap了');
}))
newObservable.subscribe(console.log);

Insertar descripción de la imagen aquí

10.tomar

Recibe un número como argumento y devuelve los primeros del flujo de datos.

ngOnInit(): void {
    
    
    of(1,2,3)
        .pipe(
            take(2)
        ).subscribe(console.log)
}

Insertar descripción de la imagen aquí

11.tomar mientras

Acepta una función condicional como parámetro y devuelve un flujo de datos que satisface la condición. Busque de adelante hacia atrás y deténgase directamente si no se cumplen las condiciones.

of(1,100,3)
    .pipe(
        takeWhile(value => value < 10)
    ).subscribe(console.log)

Insertar descripción de la imagen aquí

12. tomar hasta

Acepta un objeto observable como parámetro. Cuando el objeto observable emite datos, la fuente de datos principal dejará de enviar datos.

// 创建一个异步发出数据的Observable
const observable= new Observable(subscriber => {
    
    
    setTimeout(() => {
    
    
        subscriber.next(1)
    },5000)
})
// 使用Interval创建一个Observable
// 当oberable发出数据时,使用takeUntil停止
interval(1000)
    .pipe(takeUntil(observable))
    .subscribe(console.log)

La consola dejará de imprimir después de imprimir 4 números.

Insertar descripción de la imagen aquí

13.tiempo del acelerador

Acelerador. Cuando los eventos observables envían datos al exterior con alta frecuencia, para evitar activar devoluciones de llamadas de suscripción con frecuencia, utilice throttleTimeoperadores para pasar un intervalo de tiempo como parámetro para garantizar que los datos solo se envíen una vez dentro del intervalo de tiempo especificado. Si se hace clic con frecuencia en el siguiente código en la página, solo se activará la función de devolución de llamada del suscriptor una vez en 1 segundo.

fromEvent(document, 'click')
    .pipe(throttleTime(1000))
    .subscribe(() => console.log('Clicked!'));
14.tiempo de rebote

Anti vibración. Acepta un intervalo de tiempo como parámetro y solo responde a los últimos datos enviados en este intervalo de tiempo. Si el evento se activa nuevamente dentro de este intervalo, la función de devolución de llamada del suscriptor no se activará. La función de devolución de llamada del suscriptor se activará solo si el evento se activa solo una vez dentro de un cierto intervalo de tiempo. Se hace clic con frecuencia en el siguiente código en la página. La función de devolución de llamada del suscriptor solo se activará cuando se haga clic en la página solo una vez en 1 segundo.

fromEvent(document, 'click')
    .pipe(debounceTime(1000))
    .subscribe(() => console.log('Clicked!'));

La limitación es adecuada para escenarios en los que los usuarios hacen clic, porque al hacer clic esperan resultados inmediatos. Para evitar clics repetidos, se agrega limitación. Anti-vibración es adecuado para escenarios de entrada del usuario, ya que retrasa la ejecución de la función de devolución de llamada porque es posible que el evento de entrada aún no haya finalizado. Por lo tanto, la limitación es adecuada para escenarios en los que una operación surte efecto y la antivibración es adecuada para escenarios en los que los resultados se obtienen mediante múltiples operaciones.

15.distinto hasta el cambio

distintivoUntilChanged
no necesita recibir parámetros. Compruebe si los datos enviados esta vez son los mismos que los datos enviados la última vez. Si son iguales se omiten, si son diferentes se pasan al suscriptor.

of(1, 1, 2, 2,3)
    .pipe(distinctUntilChanged())
    .subscribe(console.log);

Insertar descripción de la imagen aquí

7. Caso 1: Arrastrar elementos con el mouse

En plantilla de componente

<style>
    #box{
      
      
        width: 100px;
        height: 100px;
        background-color: pink;
        position: absolute;
        left: 0;
        top: 0;
    }
</style>
<!--使用#标识需要在组件类中获取的html元素-->
<div id="box" #box></div>

En clase de componente

// 实现元素拖拽
// 1.鼠标按下的时候,计算鼠标相对于box的位置,即鼠标相对于视口的位置-box相对于视口的偏移量
// 2.为document增加mousemove事件,并计算鼠标相对于视口的位置:clientX和clientY
// 3.鼠标移动的时候,计算鼠标相对于视口的位置:clientX和clientY
// 4.设置box的偏移量:鼠标相对于视口的位置-鼠标相对于box的位置
// 5.鼠标抬起的时候,移除mousemove事件

// 使用ViewChild获取dom元素
@ViewChild('box') box: ElementRef | undefined;

ngAfterViewInit() {
    
    
    // 移动事件
    const moveObservable = fromEvent(document, 'mousemove')
        .pipe(takeUntil(fromEvent(document, 'mouseup')))
    // this.box.nativeElement为dom元素。?为可选链,防止报错
    // fromEvent为dom元素绑定事件并将事件转为Observable
    fromEvent(this.box?.nativeElement, 'mousedown').pipe(
        // 处理数据的操作都放在操作符中
        map(event => ({
    
    
        	// 获取鼠标相对于box的位置
            distanceX: (event as MouseEvent).clientX - this.box?.nativeElement.offsetLeft,
            distanceY: (event as MouseEvent).clientY - this.box?.nativeElement.offsetTop
        })),
        // 流式操作,这里获取的是map处理后的结果
        // 使用对象结构赋值直接获取distanceX和distanceY
        switchMap(({
    
    distanceX, distanceY}) =>
            fromEvent(document, 'mousemove').pipe(
                // 当鼠标抬起时停止移动事件数据的发送
                takeUntil(fromEvent(document, 'mouseup')),
                map(moveEvent => ({
    
    
                        left: (moveEvent as MouseEvent).clientX - distanceX,
                        top: (moveEvent as MouseEvent).clientY - distanceY
                    })
                )
            )))
        // 使用解构赋值直接获取left和top
        .subscribe(({
    
    left, top}) => {
    
    
            this.box!.nativeElement.style.left = left + 'px';
            this.box!.nativeElement.style.top = top + 'px';
        })
}

8. Caso 2: Búsqueda

En plantilla de componente

<input type="text" placeholder="请输入搜索内容" #search>

En clase de componente

@ViewChild('search') search: ElementRef | undefined;

ngAfterViewInit() {
    
    
    fromEvent(this.search?.nativeElement, 'input')
        .pipe(
            debounceTime(1000), // 防抖。适用于输入框,防止用户输入过快
            map((event: any) => event.target.value), // 获取输入的内容
            distinctUntilChanged(), // 过滤掉重复的值。如果用户删除之后快速输入,和上次的值一样,就阻止触发请求
            switchMap((value: string) =>
                this.response(value) // switchMap应该返回Observable,但是这里返回Promise也正常运行了,应该是能自动转成Obervable
            )
        )
        .subscribe((res: any) => {
    
    
                console.log(res)
            }
        )
}

// 模拟一个请求
private response(data: any) {
    
    
    return new Promise((resolve, reject) => {
    
    
        setTimeout(() => {
    
    
            resolve({
    
    
                data: data,
                status: 200
            })
        }, 1000)
    })
}

9. Caso 3: Solicitud en serie

Hacer clic en un botón requiere enviar dos solicitudes en secuencia. Los parámetros de la segunda solicitud deben depender del valor de retorno de la primera solicitud.
Plantilla de componente

<button #btn>点击获取数据</button>

Clase de componente

@ViewChild('btn') btn: ElementRef | undefined;

ngAfterViewInit() {
    
    
    fromEvent(this.btn?.nativeElement, 'click')
        .pipe(
            concatMap(event=>this.response('Hello')),
            pluck('data'),
            concatMap((data: any) => this.response(data + ' Angular'))
        )
        .subscribe((res: any) => {
    
    
                console.log(res)
            }
        )
}

// 模拟一个请求
private response(data: any) {
    
    
    return new Promise((resolve, reject) => {
    
    
        setTimeout(() => {
    
    
            resolve({
    
    
                data: data,
                status: 200
            })
        }, 1000)
    })
}

Después de hacer clic, obtienes el resultado de la segunda solicitud.

Insertar descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_45855469/article/details/130991565
Recomendado
Clasificación