ไลบรารี RxJS ช่วยแนะนำคุณสมบัติการเขียนโปรแกรมเชิงโต้ตอบและสิ่งที่สังเกตได้ให้กับ JavaScript หากคุณเป็นเหมือนฉัน คุณคงเคยได้ยินเรื่องนี้เป็นครั้งแรกในขณะที่ทำงานกับ Angular เนื่องจากฟีเจอร์หลายอย่างของเฟรมเวิร์กนั้นใช้ Observables ทันทีที่แกะกล่อง

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

  1. คิดว่า Observables เป็นอาร์เรย์แบบอะซิงโครนัส
  2. คิดว่าตัวดำเนินการเป็นช่องทาง

มาสำรวจรายละเอียดเหล่านี้กันดีกว่า เพื่อให้คุณสามารถเริ่มใช้งานได้ตั้งแต่วันนี้!

1. คิดว่า Observables เป็นอาร์เรย์แบบอะซิงโครนัส

RxJS.dev กำหนดสิ่งที่สังเกตได้ดังนี้:

“สิ่งที่สังเกตได้คือคอลเล็กชั่น Push ที่ขี้เกียจของหลายค่า”

แม้ว่าจะกระชับและแม่นยำมาก แต่คำจำกัดความนี้ยังคงละทิ้งแนวคิดเชิงบริบทที่ขาดหายไปจำนวนมากเพื่อทำความเข้าใจการใช้งาน Observables ในทางปฏิบัติอย่างถ่องแท้

โชคดีที่ในการพูดคุยผ่าน Meetup เมื่อไม่นานมานี้ Abraham Williams ได้นำเสนอเรื่อง Observables และ Angular โดยเขาได้แสดงแผนภาพเล็กๆ ที่กลายเป็น อ๋อ ขนาดใหญ่มาก! ช่วงเวลาที่ฉันเข้าใจสิ่งที่สังเกตได้คืออะไร ในนั้น เขาได้กล่าวถึงจุดตัดของตัวแปรเดี่ยว/หลายค่า และตัวแปรซิงโครนัส/อะซิงโครนัส เขาทำสิ่งนี้เพื่อชี้ให้เห็นว่า Observables พอดีกับภาพโดยแบ่งย่อยดังนี้:

นี่คือจุดที่ทุกอย่างชัดเจน: ตามแนวคิดแล้ว Observables สามารถถือเป็นอาร์เรย์แบบอะซิงโครนัสได้!

ตอนนี้ สิ่งที่ทำให้สิ่งต่างๆ คลิก ไม่เพียงแต่เห็นว่าเป็นตัวแปรหลายค่า แต่ยังเห็นว่า Observables แตกต่างจาก "Promises" อย่างไร สัญญาได้รับการสนับสนุนโดยธรรมชาติใน JavaScript เพื่อจัดการค่าอะซิงโครนัสเดียว ดังนั้นเมื่อแก้ไขสัญญาแล้ว ก็เสร็จสิ้น ตัวอย่างเช่น สนิปต่อไปนี้จะสร้าง Promise ที่ส่งคืน 'บี๊บ' เมื่อแก้ไขหลังจากผ่านไปหนึ่งวินาที (จากนั้นเราจะนำสตริงนั้นและพิมพ์ไปที่คอนโซล)

ประเด็นหลักที่นี่คือเพื่อดูว่าเมื่อสัญญาได้รับการแก้ไขแล้วและจะไม่ปล่อยสิ่งอื่นใดอีก

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

ตัวอย่างเช่น ตัวอย่างด้านล่างสร้าง Observable ซึ่งส่งเสียงสามสาย ('บี๊บ', 'boop', 'bop') ก่อนที่จะเสร็จสิ้น:

อย่างที่คุณเห็น สองค่าแรก ('บี๊บ', 'boop') จะถูกปล่อยออกมาทีละค่า แต่ค่าที่สาม ('bop') จะถูกปล่อยออกมาหลังจากผ่านไปสิบวินาที ในช่วงเวลานั้น subscription จะยังคง "ถ่ายทอดสด" เพื่อรอสิ่งอื่นมาสตรีม

อีกตัวอย่างหนึ่ง: ลองจินตนาการว่าเราต้องการพิมพ์เพื่อควบคุมพิกัดของเคอร์เซอร์ของเมาส์ทุกครั้งที่ผู้ใช้คลิกบนหน้าเว็บ

เราสามารถทำได้สำเร็จโดยใช้ตัวดำเนินการสร้าง RxJS fromEvent() ซึ่งช่วยให้เราสามารถสร้าง Observable ที่ปล่อยข้อมูลเหตุการณ์ทุกครั้งที่มีการทริกเกอร์เหตุการณ์ขององค์ประกอบ DOM ที่ระบุ (ในกรณีนี้ document's MouseClick event):

เมื่อเราสมัครรับข้อมูล observable มันจะเริ่มปล่อยข้อมูลเหตุการณ์ในแต่ละเหตุการณ์การคลิก จากนั้นเราจะพิมพ์เพื่อควบคุมพิกัดของเคอร์เซอร์ในขณะที่เกิดเหตุการณ์การคลิก

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

คุณก็เข้าใจแล้ว: Observable เปรียบเสมือนอาร์เรย์ของข้อมูลอะซิงโครนัส

2. คิดว่าตัวดำเนินการ RxJS เป็นช่องทาง

แบบจำลองทางจิตอีกแบบหนึ่งที่ช่วยให้ฉันเข้าใจวิธีจัดการสิ่งที่สังเกตได้ดีขึ้นมาจาก วิดีโอนี้โดย Maximilian Schwarzmüller จาก Academind

ในวิดีโอ เขาแนะนำให้คิดว่า "ตัวดำเนินการ RxJS" เป็น ช่องทาง ที่รับค่าที่ปล่อยออกมาจาก Observable และดำเนินการบางอย่างกับค่าเหล่านั้นก่อนที่จะส่งผ่านไปยังสตรีม (เช่น การปรับเปลี่ยนค่าเหล่านั้น การสร้างผลข้างเคียง ฯลฯ ).

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

อย่างที่คุณเห็น เรากำลังใช้เมธอด pipe() ของ Observable เพื่อตั้งค่ารายการโอเปอเรเตอร์ คล้ายช่องทาง ตามลำดับ เพื่อช่วยเราจัดการค่าที่ปล่อยออกมาแต่ละค่า อย่างไรก็ตาม ก่อนที่เราจะเจาะลึกโค้ด ก่อนอื่นเรามาดูกันว่าโอเปอเรเตอร์แต่ละรายทำอะไรได้บ้าง:

  • bufferTime(): ตัวดำเนินการที่ "เก็บ" ค่าทั้งหมดที่ Observable ปล่อยออกมาตามระยะเวลาที่กำหนด จากนั้นส่งคืนค่าเหล่านั้นในอาร์เรย์หลังจากเวลาผ่านไป
  • map(): หนึ่งในโอเปอเรเตอร์ที่ใช้บ่อยที่สุด map() จะทำงานทุกครั้งที่ Observable ส่งค่าออกมา และช่วยให้เราดำเนินการบางอย่างด้วยค่านั้นได้ มันจะล้อมทุกสิ่งที่เราส่งคืนใน Observable ใหม่โดยอัตโนมัติ เพื่อให้สามารถจัดการต่อไปในบรรทัดได้
  • filter(): อนุญาตให้ค่าที่ปล่อยออกมาจาก Observable ดำเนินการสตรีมต่อไปหากตรงตามเงื่อนไขการกรองที่ต้องการ

ตอนนี้เรารู้แล้วว่าโอเปอเรเตอร์แต่ละตัวทำอะไร ต่อไปนี้คือวิธีที่เราใช้โอเปอเรเตอร์เหล่านี้ในโค้ดด้านบนเพื่อบรรลุเป้าหมายของเรา:

ในแต่ละเหตุการณ์การคลิก:

  1. ใช้ bufferTime()เพื่อเริ่มอาร์เรย์ของเหตุการณ์การคลิก และรออีก 250 มิลลิวินาทีเพื่อดูว่ามีเหตุการณ์การคลิกอื่นๆ เกิดขึ้นหรือไม่ โดยรีเซ็ตเวลาบัฟเฟอร์ในการคลิกแต่ละครั้ง เมื่อไม่มีการคลิกเกิดขึ้นอีกหลังจากผ่านไป 250 มิลลิวินาที ให้ส่งคืน Observable ใหม่พร้อมกับอาร์เรย์ของเหตุการณ์การคลิก
  2. ใช้ map() เพื่อรับอาร์เรย์ของค่าและส่งกลับ Observable ใหม่โดยมีค่าเฉพาะความยาวของอาร์เรย์เป็นค่า
  3. ใช้ filter() เพื่ออนุญาตให้ค่าดำเนินต่อไปตามสตรีมหากความยาวของอาร์เรย์มากกว่าหรือเท่ากับ 2(เช่น "ดับเบิลคลิก")
  4. ในบล็อก subscribe() ให้พิมพ์ 'double click!' ไปที่คอนโซลสำหรับแต่ละเหตุการณ์ที่ตรงตามเงื่อนไขการกรองสำเร็จ

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

ดังนั้น: ตัวดำเนินการก็เหมือนกับช่องทางที่ช่วยเราจัดการกับกระแสของค่าที่ปล่อยออกมาจาก Observable

แน่นอนว่ารายชื่อตัวดำเนินการ RxJS นั้นค่อนข้างใหญ่ และต้องใช้เวลาสักพักกว่าจะเข้าใจว่าแต่ละตัวทำอะไร แต่การมองว่ามันเป็นช่องทางคือสิ่งที่ช่วยให้ฉันเรียนรู้วิธีใช้งานพวกมันได้อย่างมีประสิทธิภาพมากขึ้น

นอกจากนี้ แบบจำลองทางจิตนี้ยังช่วยให้ฉันระบุได้อย่างกว้างๆ ว่าผู้ให้บริการประเภทใดบ้าง:

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

บทสรุป

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

ฉันหวังว่านี่จะช่วยได้ และขอให้มีความสุขกับการเขียนโค้ด!

ขอขอบคุณไมค์ กอยเนส

สร้างเว็บแอปพลิเคชันที่ประกอบได้

อย่าสร้างเว็บใหญ่โต ใช้ Bit เพื่อสร้างและเขียนส่วนประกอบซอฟต์แวร์ที่แยกส่วน — ในเฟรมเวิร์กที่คุณชื่นชอบ เช่น React หรือ Node สร้างส่วนหน้าและส่วนหลังที่ปรับขนาดได้ด้วยประสบการณ์การพัฒนาที่ทรงพลังและสนุกสนาน

นำทีมของคุณไปที่ Bit Cloud เพื่อโฮสต์และทำงานร่วมกันในส่วนประกอบต่างๆ ร่วมกัน และเร่งความเร็ว ขยายขนาด และสร้างมาตรฐานการพัฒนาเป็นทีมได้อย่างมาก เริ่มต้นด้วยส่วนหน้าที่เขียนได้ เช่น Design System หรือ Micro Frontends หรือสำรวจแบ็กเอนด์ที่เขียนได้ ลองดูสิ →

เรียนรู้เพิ่มเติม