성공과 실패 나누기

FxDart 승 async

요구사항

2026-08 임포트에서 온 주문 일곱 건이 비동기 검증을 거치는데, 그중 두 건에서 오류가 던져집니다(배송 주소 누락, 알 수 없는 SKU). 양쪽 결과를 모두 지키세요: 유효한 주문마다 ok: 줄을 출력한 뒤, 실패 건수를 출력합니다. 데이터는 코드에 들어 있습니다; 두 버전 모두 예상 출력 아래에 표시된 줄들을 출력해야 합니다.

예상 출력
ok: order #1001
ok: order #1002
ok: order #1004
ok: order #1005
ok: order #1007
failed: 2

나란히 보기

RxDart

FxDart

차이가 나는 이유

스트림에는 채널이 둘 있습니다 — 데이터와 오류 — 그리고 오류 채널은 스트림 전체 단위의 의미론을 지닙니다: 검증 하나가 오류를 던지면 주문 다섯 건이 처리되지 않은 채 파이프라인이 죽습니다. 그래서 RxDart 쪽은 단순히 asyncMap(validate)을 할 수 없습니다; 검증을 자기만의 내부 스트림(Rx.fromCallable)으로 감싸고, 그 내부 오류 채널에서 onErrorReturnWith로 잡아, 실패를 데이터 값으로 재인코딩한 뒤에야 다시 병합합니다. 복구는 동작하지만, 이것은 채널 배관 공사입니다: 오류가 데이터 경로를 떠났다가 호위를 받아 되돌아와야 했던 것입니다.

FxDart 쪽은 실패를 별도 채널에 올리는 일이 애초에 없습니다. map 안의 try/catch 하나가 각 결과를 평범한 레코드 — (id, error?) — 로 바꾸고, 거기서부터 partition은 평범한 술어 분할입니다. 이것이 오류에 대한 풀 모델의 일반적인 태도입니다: 오류는 다른 모든 것과 같은 타입 있는 파이프라인을 흐르는 이므로, 성공과 실패를 함께 지키는 데 아무 비용도 들지 않습니다. 결과의 성패가 중단이 아니라 결과의 일부일 때는, 특권적인 오류 채널이 없는 모델 쪽이 되돌릴 것이 적습니다.

벤치마크

Apple M1 Max, RAM 32 GB · Dart 3.12.2 (AOT 컴파일) · 2026-08-18

비동기 예제입니다. 대표 스케일이 1,000,000이 아니라 N = 10,000인 이유 — 원소 하나마다 양쪽 모두 이벤트 루프를 한 바퀴 돌아야 하므로, 실제 await 백만 번은 파이프라인이 아니라 Dart 이벤트 루프를 몇 분씩 재게 됩니다. 지연은 0으로 두고 예제의 동시성 제한은 그대로 유지합니다. 막대가 비교하는 것은 파이프라인 기계 장치 자체입니다.

N = 100

시간 무승부

RxDart 818 µs
FxDart 430 µs

최대 메모리 무승부

RxDart 17.0 MB
FxDart 16.6 MB

N = 10,000

시간 FxDart 승

RxDart 65.2 ms
FxDart 34.8 ms

최대 메모리 RxDart 승

RxDart 25.2 MB
FxDart 30.6 MB

막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.