ท่อสำหรับ RxJS คืออะไร?


110

ฉันคิดว่าฉันมีแนวคิดพื้นฐาน แต่มีบางอย่างที่ไม่ชัดเจน

โดยทั่วไปนี่คือวิธีที่ฉันใช้Observable:

observable.subscribe(x => {

})

หากฉันต้องการกรองข้อมูลฉันสามารถใช้สิ่งนี้:

import { first, last, map, reduce, find, skipWhile } from 'rxjs/operators';
observable.pipe(
    map(x => {return x}),
    first()
    ).subscribe(x => {

})

ฉันยังสามารถทำได้:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';

observable.map(x => {return x}).first().subscribe(x => {

})

ดังนั้นคำถามของฉันคือ:

  1. อะไรคือความแตกต่าง?
  2. หากไม่มีความแตกต่างทำไมฟังก์ชันจึงpipeมีอยู่?
  3. เหตุใดฟังก์ชันเหล่านี้จึงต้องการการนำเข้าที่แตกต่างกัน

1
ฉันกำลังจะบอกว่ามันมีไว้สำหรับตัวดำเนินการที่กำหนดเองไม่ใช่เจ้าของภาษา แต่ฉันไม่รู้ด้วยซ้ำว่าถูกต้องหรือไม่ ไม่pipe()ปล่อยให้คุณผ่านผู้ประกอบการที่คุณสร้างหรือไม่?
zero298

คำตอบ:


73

ตัวดำเนินการ "pipable" (เดิมคือ "lettable") เป็นวิธีการใช้ตัวดำเนินการในปัจจุบันและที่แนะนำตั้งแต่ RxJS 5.5

ขอแนะนำให้คุณอ่านเอกสารอย่างเป็นทางการhttps://rxjs.dev/guide/v6/pipeable-operators

ความแตกต่างที่สำคัญคือการสร้างตัวดำเนินการแบบกำหนดเองนั้นง่ายกว่าและสามารถสร้างต้นไม้ได้ดีกว่าในขณะที่ไม่ได้ปรับเปลี่ยนทั่วโลก Observableวัตถุที่อาจทำให้เกิดการชนกันได้หากสองฝ่ายที่แตกต่างกันต้องการสร้างตัวดำเนินการที่มีชื่อเดียวกัน

การใช้importคำสั่งแยกกันสำหรับแต่ละโอเปอเรเตอร์'rxjs/add/operator/first'เป็นวิธีการสร้าง App Bundle ขนาดเล็ก การอิมพอร์ตเฉพาะตัวดำเนินการที่คุณต้องการแทนที่จะเป็นไลบรารี RxJS ทั้งหมดคุณสามารถลดขนาดบันเดิลทั้งหมดได้อย่างมาก อย่างไรก็ตามคอมไพลเลอร์ไม่สามารถทราบได้ว่าคุณนำเข้า'rxjs/add/operator/first'เพราะคุณต้องการมันจริงๆในโค้ดของคุณหรือคุณลืมที่จะลบออกเมื่อทำการรีแฟคโค้ดของคุณ นั่นเป็นข้อดีอย่างหนึ่งของการใช้ตัวดำเนินการ pipable ซึ่งการนำเข้าที่ไม่ได้ใช้จะถูกละเว้นโดยอัตโนมัติ


1
เกี่ยวกับการยืนยันของคุณunused imports are ignored automaticallyปัจจุบัน IDE มีปลั๊กอินที่ลบการนำเข้าที่ไม่ได้ใช้
silvanasono

ไม่ใช่ทุกคนที่ใช้ IDE หรือปลั๊กอินเหล่านี้หลายคนใช้โปรแกรมแก้ไขข้อความพื้นฐาน อาจเป็นเวลาส่วนใหญ่ที่เราไม่สามารถถ่ายทอดคำสั่งที่ว่าทุกคนในทีมใช้ IDE / ชุดปลั๊กอิน / โปรแกรมแก้ไขข้อความเดียวกันกับที่เราเป็นอยู่
Adam Faryna

3
@AdamFaryna แน่นอนบางทีมอาจเขียนโค้ดลงบนกระดาษด้วย แต่ทำไมพวกเขาถึงมีเครื่องมือที่ทันสมัย การใช้โปรแกรมแก้ไขข้อความโดยเฉพาะอย่างยิ่งการไม่มีปลั๊กอินที่สำคัญจะคล้ายกับการเขียนโค้ดบนกระดาษ คุณทำได้ แต่ทำไมทีม / นักพัฒนาที่ดีถึงทำเช่นนั้น
Denes Papp

โปรแกรมแก้ไขโค้ด @DenesPapp ไม่สำคัญตราบใดที่ผู้คนสามารถใช้งานได้อย่างมีประสิทธิผล นอกเหนือจากนั้นเป็นเพียงความชอบส่วนบุคคล การเปรียบเทียบของคุณกับการเขียนโค้ดบนกระดาษไม่ถูกต้องคุณไม่สามารถรันโค้ดบนกระดาษได้ แต่โค้ดที่เขียนด้วยโปรแกรมแก้ไขข้อความใดก็ได้
Adam Faryna

1
@perymimon คุณทำได้ แต่ต้องติดตั้งrxjs-compatแพ็คเกจgithub.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
martin

16

วิธีท่อ

ตามเอกสารต้นฉบับ

ตัวดำเนินการ pipable คือฟังก์ชันรับสิ่งที่สังเกตได้เป็นอินพุตและส่งกลับค่าที่สังเกตได้อื่นที่สังเกตได้ก่อนหน้านี้จะไม่ถูกแก้ไข

pipe(...fns: UnaryFunction<any, any>[]): UnaryFunction<any, any>

โพสต์ต้นฉบับ

ท่อหมายถึงอะไร?

นั่นหมายความว่าผู้ประกอบการใด ๆ rxjs/operatorsที่คุณเคยใช้ในตัวอย่างของการสังเกตจะมีฟังก์ชั่นที่บริสุทธิ์ภายใต้ สิ่งนี้ทำให้การสร้างองค์ประกอบของตัวดำเนินการหรือการใช้ตัวดำเนินการซ้ำกลายเป็นเรื่องง่ายโดยไม่ต้องหันไปใช้ยิมนาสติกการเขียนโปรแกรมทุกประเภทซึ่งคุณต้องสร้าง Observable ที่สามารถสังเกตได้แบบกำหนดเองจากนั้นเขียนทับลิฟท์เพื่อสร้างสิ่งที่คุณกำหนดเอง

const { Observable } = require('rxjs/Rx')
const { filter, map, reduce,  } = require('rxjs/operators')
const { pipe } = require('rxjs/Rx')

const filterOutWithEvens = filter(x => x % 2)
const doubleByValue = x => map(value => value * x);
const sumValue = reduce((acc, next) => acc + next, 0);
const source$ = Observable.range(0, 10)

source$.pipe(
  filterOutWithEvens, 
  doubleByValue(2), 
  sumValue)
  .subscribe(console.log); // 50

@VladKuts เปลี่ยนรหัสและคุณสมบัติที่กำหนดขออภัยในความไม่สะดวก
Chanaka Weerasinghe

ขอบคุณฉันไม่รู้ด้วยซ้ำว่าฉันสามารถจัดเก็บตัวดำเนินการที่สามารถไปป์ไลน์เป็นข้อมูลอ้างอิงฟังก์ชันและใช้ในการโทรไปป์ () ได้ มันสะอาดกว่าการทำแบบอินไลน์เสมอ
อเล็กซ์

10

บทสรุปที่ดีที่ฉันคิดขึ้นคือ:

มันแยกการดำเนินการสตรีมมิ่ง (แผนที่, ตัวกรอง, ลด ... ) ออกจากฟังก์ชันหลัก (การสมัครสมาชิก, การวางท่อ) ด้วยการเดินท่อแทนการผูกมัดจะไม่ก่อให้เกิดมลพิษต่อต้นแบบของ Observable ทำให้ง่ายต่อการเขย่าต้นไม้

ดูhttps://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#why

ปัญหาเกี่ยวกับตัวดำเนินการที่ได้รับการแก้ไขสำหรับ dot-chaining ได้แก่ :

ไลบรารีใด ๆ ที่อิมพอร์ตตัวดำเนินการแพตช์จะเพิ่ม Observable.prototype สำหรับผู้บริโภคทั้งหมดของไลบรารีนั้นโดยสร้างการอ้างอิงแบบตาบอด หากห้องสมุดนำการใช้งานออกไปก็จะทำให้คนอื่นเสียหายโดยไม่รู้ตัว ด้วย pipeables คุณต้องนำเข้าตัวดำเนินการที่คุณต้องการลงในแต่ละไฟล์ที่คุณใช้

โอเปอเรเตอร์ที่ติดตั้งลงบนต้นแบบโดยตรงจะไม่ "ทำให้ต้นไม้สั่นได้" ด้วยเครื่องมือเช่นค่าสะสมหรือเว็บแพ็ก ตัวดำเนินการแบบท่อจะเป็นเพราะเป็นเพียงฟังก์ชันที่ดึงเข้ามาจากโมดูลโดยตรง

ไม่สามารถตรวจพบโอเปอเรเตอร์ที่ไม่ได้ใช้ที่กำลังนำเข้าในแอพได้อย่างน่าเชื่อถือด้วยเครื่องมือสร้างหรือกฎผ้าสำลีประเภทใด ๆ นั่นหมายความว่าคุณอาจนำเข้าการสแกน แต่หยุดใช้งานและยังคงเพิ่มลงในชุดผลลัพธ์ของคุณ ด้วยตัวดำเนินการที่ต่อท่อได้หากคุณไม่ได้ใช้มันกฎผ้าสำลีสามารถเลือกมันให้คุณได้

องค์ประกอบการทำงานยอดเยี่ยม การสร้างโอเปอเรเตอร์แบบกำหนดเองของคุณจะกลายเป็นเรื่องง่ายขึ้นมากและตอนนี้ก็ใช้งานได้และดูเหมือนโอเปอเรเตอร์อื่น ๆ ทั้งหมดจาก rxjs คุณไม่จำเป็นต้องขยายการยกที่สังเกตได้หรือแทนที่การยกอีกต่อไป


9

อะไรคือความแตกต่าง? ดังที่คุณเห็นในตัวอย่างความแตกต่างหลักคือการปรับปรุงความสามารถในการอ่านซอร์สโค้ด มีเพียงสองฟังก์ชันในตัวอย่างของคุณ แต่ลองนึกดูว่ามีฟังก์ชันมากมายหรือไม่? แล้วมันจะเป็นอย่างไร

function1().function2().function3().function4()

มันดูน่าเกลียดและอ่านยากโดยเฉพาะอย่างยิ่งเมื่อคุณกรอกข้อมูลลงในฟังก์ชัน ยิ่งไปกว่านั้นตัวแก้ไขบางตัวเช่นโค้ด Visual Studio ไม่อนุญาตให้มีความยาวมากกว่า 140 บรรทัด แต่ถ้าเป็นไปตามนั้น

Observable.pipe(
function1(),
function2(),
function3(),
function4()
)

สิ่งนี้ช่วยเพิ่มความสามารถในการอ่านได้อย่างมาก

ถ้าไม่มีความแตกต่างทำไมท่อฟังก์ชันจึงมีอยู่? จุดประสงค์ของฟังก์ชัน PIPE () คือการรวมตัวกันฟังก์ชันทั้งหมดที่ใช้และส่งกลับที่สังเกตได้ เริ่มต้นที่สังเกตได้จากนั้นสิ่งที่สังเกตได้จะถูกใช้ตลอดทั้งฟังก์ชัน pipe () โดยแต่ละฟังก์ชันที่ใช้ภายใน

ฟังก์ชันแรกรับสิ่งที่สังเกตได้ประมวลผลแก้ไขค่าและส่งผ่านไปยังฟังก์ชันถัดไปจากนั้นฟังก์ชันถัดไปจะนำเอาท์พุทที่สังเกตได้ของฟังก์ชันแรกประมวลผลและส่งผ่านไปยังฟังก์ชันถัดไปจากนั้นจะดำเนินการต่อไปจนกว่าฟังก์ชันทั้งหมด ภายในของฟังก์ชัน pipe () ใช้ที่สังเกตได้ในที่สุดคุณก็มีการประมวลผลที่สังเกตได้ ในตอนท้ายคุณสามารถเรียกใช้ฟังก์ชันที่สังเกตได้ด้วย subscribe () เพื่อดึงค่าออกมา โปรดจำไว้ว่าค่าที่สังเกตได้เดิมจะไม่มีการเปลี่ยนแปลง !! 

ทำไมฟังก์ชันเหล่านั้นจึงต้องการการนำเข้าที่แตกต่างกัน? การนำเข้าขึ้นอยู่กับว่าฟังก์ชันถูกระบุในแพ็คเกจ rxjs มันเป็นแบบนี้ โมดูลทั้งหมดจะถูกเก็บไว้ในโฟลเดอร์ node_modules ใน Angular นำเข้า {class} จาก "module";

ลองใช้รหัสต่อไปนี้เป็นตัวอย่าง ฉันเพิ่งเขียนมันใน stackblitz จึงไม่มีการสร้างขึ้นโดยอัตโนมัติหรือคัดลอกจากที่อื่น ฉันไม่เห็นจุดที่จะคัดลอกสิ่งที่ระบุไว้ในเอกสาร rxjs เมื่อคุณสามารถไปอ่านได้ด้วย ฉันถือว่าคุณถามคำถามนี้ที่นี่เนื่องจากคุณไม่เข้าใจเอกสาร 

  • มีไปป์ที่สังเกตได้ของคลาสแผนที่ที่นำเข้าจากโมดูลที่เกี่ยวข้อง 
  • ในเนื้อหาของคลาสฉันใช้ฟังก์ชัน Pipe () ตามที่เห็นในโค้ด 
  • ฟังก์ชัน Of () จะส่งคืนค่าที่สังเกตได้ซึ่งจะแสดงตัวเลขตามลำดับเมื่อสมัครใช้งาน

  • ยังไม่ได้สมัครสมาชิก

  • เมื่อคุณใช้มันชอบ Observable.pipe () ฟังก์ชัน pipe () จะใช้ Observable ที่กำหนดเป็นอินพุต

  • ฟังก์ชันแรกฟังก์ชัน map () ใช้ Observable นั้นประมวลผลส่งคืน Observable ที่ประมวลผลแล้วกลับไปที่ฟังก์ชัน pipe ()

  • จากนั้น Observable ที่ประมวลผลแล้วจะถูกมอบให้กับฟังก์ชันถัดไปหากมี

  • และมันจะดำเนินต่อไปเช่นนั้นจนกว่าฟังก์ชันทั้งหมดจะประมวลผล Observable

  • ในตอนท้ายที่ Observable จะถูกส่งกลับโดยฟังก์ชัน pipe () ไปยังตัวแปรในตัวอย่างต่อไปนี้เป็น obs

ตอนนี้สิ่งที่อยู่ใน Observable คือตราบใดที่ผู้สังเกตการณ์ไม่ได้สมัครมันก็จะไม่ปล่อยคุณค่าใด ๆ ดังนั้นฉันจึงใช้ฟังก์ชัน subscribe () เพื่อสมัครสมาชิก Observable นี้จากนั้นทันทีที่ฉันสมัครสมาชิก ฟังก์ชัน of () เริ่มเปล่งค่าจากนั้นจะถูกประมวลผลผ่านฟังก์ชัน pipe () และคุณจะได้ผลลัพธ์สุดท้ายในตอนท้ายเช่น 1 นำมาจากฟังก์ชัน () ฟังก์ชัน 1 ถูกเพิ่ม 1 ในฟังก์ชัน map () และกลับมา คุณสามารถรับค่านั้นเป็นอาร์กิวเมนต์ภายในฟังก์ชันสมัครสมาชิก (ฟังก์ชัน ( อาร์กิวเมนต์ ) {})

หากคุณต้องการพิมพ์ให้ใช้เป็น

subscribe( function (argument) {
    console.log(argument)
   } 
)
    import { Component, OnInit } from '@angular/core';
    import { pipe } from 'rxjs';
    import { Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit  {
    
      obs = of(1,2,3).pipe(
      map(x => x + 1),
      ); 
    
      constructor() { }
    
      ngOnInit(){  
        this.obs.subscribe(value => console.log(value))
      }
    }

https://stackblitz.com/edit/angular-ivy-plifkg

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.