Skip to content

withTimeout / deferred

자바스크립트가 갖추지 않은 프로미스 부품입니다. 밖에서 결말을 지어 주는 프로미스와, 한도를 둔 기다림.

API

함수설명
deferred<T>(){ promise, resolve, reject }. 밖에서 결말을 지을 수 있습니다
withTimeout(promise, ms, options?)ms 안에 결말이 나지 않으면 TimeoutError로 거부합니다
withTimeoutFallback(promise, ms, fallback, onTimeout?)거부하는 대신 fallback으로 이행합니다
delay(ms)ms 뒤에 이행합니다
TimeoutErrorwithTimeout이 던지는 오류 클래스

withTimeout options

옵션설명기본값
message오류 메시지operation timed out after {ms}ms
onTimeout기한이 지날 때 불립니다. 하던 일을 거두기 위한 자리입니다

예시

요청에 한도를 두고, 시간이 다하면 끊기

js
import { withTimeout } from 'ranuts';

const controller = new AbortController();
const res = await withTimeout(fetch(url, { signal: controller.signal }), 5000, {
  message: 'fetch timed out',
  onTimeout: () => controller.abort(),
});

실패시키는 대신 눈높이를 낮추기

js
import { withTimeoutFallback } from 'ranuts';

// 저장이 더디면 흐름을 끊는 대신 원래 파일을 돌려줍니다.
const file = await withTimeoutFallback(editor.requestSave(), 60_000, originalFile);

콜백에서 프로미스의 결말 짓기

js
import { deferred } from 'ranuts';

const ready = deferred();
sdk.onReady((editor) => ready.resolve(editor));
sdk.onError((error) => ready.reject(error));

const editor = await ready.promise;

기한을 둔 작업을 차례로 세우기

js
import { QuestQueue, withTimeout } from 'ranuts';

const queue = new QuestQueue({ simultaneous: 1 });
await queue.add(() => withTimeout(recreateEditor(config), 30_000));

참고

  1. 타이머는 언제나 치웁니다. 일 쪽이 경주에서 이겼을 때도 마찬가지입니다. 흔히 손수 짜는 꼴(Promise.race([task, new Promise((_, r) => setTimeout(r, ms))]))은 일이 먼저 끝날 때마다 타이머를 흘립니다. Node에서는 그것이 기한이 다할 때까지 프로세스를 살려 두고, 테스트에서는 다음 테스트로 발화하는 떠돌이 타이머를 남깁니다.

  2. 시간이 다해도 하던 일이 취소되지는 않습니다. 프로미스는 취소할 수 없기 때문입니다. fetch를 끊고, 워커를 끝내고, 연결을 닫는 일은 onTimeout에서 합니다.

  3. withTimeoutFallback이 삼키는 것은 기한뿐입니다. 감싼 프로미스가 진짜로 거부하면 그대로 밖으로 퍼집니다. 시간이 다한 것은 오류가 아니지만, 오류는 여전히 오류입니다.

  4. delay는 맨 setTimeout을 씁니다. 그래서 Node에서도 웹 워커에서도 브라우저에서도 똑같이 됩니다. window.setTimeout은 문서 밖에서 예외를 냅니다.

  5. deferred가 바깥의 let보다 낫습니다. 실행자의 인자를 밖에 선언한 변수에 넣는 것이 흔한 대안이지만, 타입스크립트는 그것들이 대입되었음을 증명하지 못하고, 눈에 잘 띄지 않게 어긋나기 쉽습니다.

MIT 라이선스로 배포됩니다.