เริ่มต้น Reactive Programming ด้วย Combine Part3: Stream and Operation

Cocodev
Lotus’s IT
Published in
3 min readDec 18, 2023

ในบทความก่อนหน้านี้ เราได้รู้จักกันไปแล้วว่า Reactive Programming มันเกิดจาก Streams + Operation ในบทความที่เรามาเจาะลึกกันว่า stream และ operation กันให้เห็นภาพชัดมากขึ้น

Stream

จากบทความแรกเราทำความรู้จัก stream กันไปบ้างแล้ว ซึ่ง stream เปรียบกับ สายน้ำของdataต่างๆที่เกิดขึ้น ณ เวลาใดๆ ดังนั้นเรามาดูตัวอย่างต่อไปนี้ที่มันคือ stream กัน

ตัวอย่าง การพิมพ์ keyboard เช่น

[h, e, l, l, o]

เราสามารถบอกว่าค่าตัวอักษร h, e, l, l, o นี้เป็นสตรีมโดย data นี้เกิดขึ้น ณ เวลาใดๆ

// Marble diagram
stream: |---(h)--(e)--(l)--(l)--(o)----------------------------|

time(s): |----|----|----|----|----|----|----|----|----|----|----|
0 1 2 3 4 5 6 7 8 9 10 11

จาก marble diagram มันอธิบายได้ว่า

  1. ค่า h เกิดขึ้น ณ วินาทีที่ 1
  2. ค่า e เกิดขึ้น ณ วินาทีที่ 2
  3. ค่า l เกิดขึ้น ณ วินาทีที่ 3
  4. ค่า l เกิดขึ้น ณ วินาทีที่ 4
  5. ค่า 0 เกิดขึ้น ณ วินาทีที่ 5

อีกตัวอย่าง จาก Tiktok event ที่เกิดขึ้นก็เป็นในรูป stream ได้เช่น

[scroll, scroll, love, scroll, favourite]

  1. Event เหล่านี้เป็น stream ได้มันจะต้องเกิดขึ้นได้มี เวลา ของ event ด้วย
// Marble diagram
stream: |-----(scroll)-(scroll)--(love)--(scroll)-(favourite)--|

time(s): |--------|--------|--------|--------|--------|--------|
0 1 2 3 4 5 6

จาก marble diagram มันอธิบายได้ว่า

  1. Event scroll เกิดขึ้น ณ วินาทีที่ 1
  2. Event scroll เกิดขึ้น ณ วินาทีที่ 2
  3. Event love เกิดขึ้น ณ วินาทีที่ 3
  4. Event scroll เกิดขึ้น ณ วินาทีที่ 4
  5. Event favourite เกิดขึ้น ณ วินาทีที่ 5

Operation

อย่างไรก็ตาม เรามีแค่ stream เรายังไม่สามารถกำหนดพฤติกรรมตามที่เราต้องการได้ มันจำเป็นต้องมีการทำอะไรสักอย่างกับ stream เช่น การบวก(+), การบวก(-) หรือที่เรียกว่า operation

การกำหนดพฤติกรรมที่สมบูรณ์ต้องประกอบไปด้วย streams บวกกับ operations จึงจะสมบูรณ์

|---------------------------------|   |---------------------------------|   
| Stream1 | + | Operation |
|---------------------------------| |---------------------------------|

ตัวอย่าง: 2 stream ที่มาจาก thermometer 2 ตัว กับ operation เพื่อหาค่า average ของอุณหภูมิเฉลี่ยจาก เครื่องวัดทั้ง 2

// Marble diagram
thermometer1: |------(30)----(27)----(26)----(25)---(25)-----(24)-----|
operation: (value1 + value2) / 2
thermometer2: |--------------(27)------------(23)------------(23)-----|

time: |-------|-------|-------|-------|-------|-------|-------|
00:00 00:30 01:00 01:30 02:00 02:30 03:00 03:30

จาก marble diagram มันอธิบายได้ว่า

stream1 ของ thermometer 1 จะมีเปลี่ยน value ทุกๆ ครึ่ง ชั่วโมง
stream2 ของ thermometer 2 จะมีเปลี่ยน value ทุกๆ 1 ชั่วโมง
operation เป็นการคำนวณหาค่าอุณหภูมิเฉลี่ย เมื่อ stream

งั้นเรามามอง ตัวอย่างนี้ในมุมมอง Code

import Combine
// stream1
let thermometer1 = PassthroughSubject<Double, Never>()
// stream2
let thermometer2 = PassthroughSubject<Double, Never>()
let combinedCancellable = Publishers.CombineLatest(thermometer1, thermometer2)
// operation
.map { temperature1, temperature2 in
return (temperature1 + temperature2) / 2
}.sink { averageTemperature in
print("Average Celsius (C): \(averageTemperature)")
}
// Time
// 00:30
thermometer1.send(30)
// 01:00
thermometer1.send(27)
thermometer2.send(27)
// 01:30
thermometer1.send(26)
// 02:00
thermometer1.send(25)
thermometer2.send(23)
// 02:30
thermometer1.send(25)
// 03:00
thermometer1.send(24)
thermometer2.send(23)

Output

// Time
01:00 Average Celsius (C): 32.5
01:30 Average Celsius (C): 32.5
02:00 Average Celsius (C): 32.5
02:00 Average Celsius (C): 32.5
02:30 Average Celsius (C): 32.5
03:00 Average Celsius (C): 32.5
03:00 Average Celsius (C): 32.5
Program ended with exit code: 0

สรุปจากตัวอย่าง เราได้ทำการกำหนดพฤติกรรมใว้สำหรับรอการไหลเข้ามาของ data และเมื่อ run มาถึง thermometer1.send(30) เป็นการจำลองว่ามีการเกิด data ณ เวลา 1:00 data ก็จะไหลเข้าไปและเกิดพฤติกรรมตามที่กำหนด

อาจจะมีคำถามเกี่ยวกับ CombineLatest จริงๆมันก็คือ Functional Reactive Programming อันหนึ่ง ซึ่งเคยได้กล่าวมาในแบบแรก แต่บทถัดไปเราจะมาลงลึกกับหัวข้อนี้ครับ

--

--