Async iterator 멀티캐스팅하기


개요

TODO: 블로그 포스팅에 그림 좀 넣는 성의를 가지자.

구현만 하면 async iterator로도 원하는 기능을 구현할 수 있다. 여기서는 async iterator를 사용하여 multicasting과 비슷한 것을 구현할 건데, 같은 기능 다시 구현하면 쓸데없으니까 요런 느낌으로 구현해볼 것이다.

  • iterator를 생성하는 함수를 반환해서, 이 함수를 여러 번 호출하면 여러 개의 iterator를 생성할 수 있게 한다.
  • 여러개의 iterator가 동시에 있다면, 걔네들 모두 같은 값을 전달받을 것이다.
  • iterator.next()가 반환된 이후에 값이 변경되지 않았다면, 다음 iterator.next()는 값이 변경될 때까지 기다렸다가 변경된 값을 반환한다.
  • iterator.next()가 반환된 이후에 값이 한 번 이상 변경되었다면, 다음 iterator.next()는 즉시 가장 마지막 값을 반환한다.

데모

FIXME: 버튼에 CSS좀 입히자. 블로그 주인장 진짜 디자인 못한다.

배경

(이하 copilot이 작성) 웹 프레임워크에서 상태 전달은 대개 Rxjs를 사용한다. Rxjs는 async interator를 지원하지 않기 때문에 Observable을 사용한다. 하지만 Rxjs의 multicastingSubject를 사용하는데, 이는 async interator를 지원하지 않는다. 이 문제를 해결하기 위해 multicasting async iterator…(야 왜 여기에 링크 있을것처럼 해놓고 링크 자동완성이 안되냐)

근데

구현

export function createListener<T>(initialValue: T): [() => AsyncGenerator<T>, (newValue: T) => void, () => T] {
	let current: T = initialValue;
	let publish: () => void = () => { };

	function setter(newValue: T): void {
		current = newValue;
		const oldPublish = publish;
		publish = () => { };
		oldPublish();
	}

	function getter(): T {
		return current;
	}

	async function* listen() {
		const id = Math.random();
		let available = true;
		while (true) {
			if (available) {
				const oldPublish = publish;
				publish = () => {
					available = true;
					oldPublish();
				};
				available = false;
				yield current;
				continue;
			}

			await new Promise<void>((resolve) => {
				const oldPublish = publish;
				publish = () => {
					resolve();
					oldPublish();
				};
			});
			// available should be true after await, as callback registered in available=true branch should be called.
		}
	}

	return [listen, setter, getter];
}

오늘의 자랑거리: Zero dependency

해보니까 Async iterator는 React에 찰싹 달라붙지 않는다. Hooks API처럼 자동으로 지켜보는게 아니라서 내부적으로 useState로 상태를 하나 더 만들어서 업데이트하도록 구현했다. 실시간 업데이트도 지원하지만, 사실 더 신경쓴 부분은 클릭할 때에만 업데이트하는 버튼이다. 만약 값이 새로 업데이트 되지 않았으면 pending 상태로 들어갔다가, 값이 업데이트되는 시점에 바로 적용되는 것이다. 잘 활용하면 관심없을 때는 업데이트를 하지 않다가, 필요할 때에만 실시간 업데이트를 한다던지 하는 기능 구현이 가능할 것 같다.

문제점

현재 구현은 처음부터 값이 준비된 상태를 가정하고 있는데, 만약 초기값을 기다려야 하는 상황이라면 조금 맞지 않다.

결론

그냥 어지간하면 Rxjs랑 관련 라이브러리로 때워라.