Skip to content

once / singleFlight

무언가를 딱 한 번만 돌리고 그 결과를 다시 씁니다. 설정 해석을 미루는 초기화, 한 번뿐인 무거운 계산, 게으른 게터 따위에 씁니다. once는 동기이고 singleFlight는 그 비동기 짝입니다.

API

once(fn)

매개변수설명타입기본값
fn한 번만 돌릴 함수. 함수가 아니면 그대로 돌려줍니다Function | any필수

감싼 함수를 돌려줍니다. 첫 호출이 값을 구해 갈무리하고, 이후의 호출은 어떤 인자를 넘기든 그 첫 결과를 돌려줍니다.

singleFlight(fn)

매개변수설명타입기본값
fn한 번만 돌릴 비동기 함수() => Promise<T>필수

감싼 함수와 함께 다음을 돌려줍니다.

멤버설명타입
reset()갈무리한 결과를 버려 다음 호출에서 fn을 다시 돌립니다() => void
started결과를 가졌는지, 아니면 지금 돌고 있는지boolean

memoize(fn)

once의 별칭이며 더는 권하지 않습니다. 아래 경고를 보세요.

예시

js
import { once, singleFlight } from 'ranuts';

// 동기: 설정은 처음 건드릴 때만 해석합니다
const config = once(() => JSON.parse(rawConfig));
config(); // 해석합니다
config(); // 갈무리한 값을 돌려줍니다

// 비동기: 부르는 쪽이 몇이든 데이터베이스는 한 번만 엽니다
const ready = singleFlight(() => db.openDataBase());
await Promise.all([ready(), ready(), ready()]); // 여는 것은 한 번

참고

  1. once는 인자별로 갈무리하지 않습니다. 효력을 갖는 것은 첫 호출의 인자뿐입니다. 인자마다 갈무리해야 한다면 Map을 직접 쓰세요.
  2. once는 값을 구한 뒤 fn을 놓아 줍니다. 그래서 그 함수가 붙들고 있던 것들이 수거될 수 있습니다.
  3. singleFlight는 실패를 갈무리하지 않습니다. 실패한 시도는 갈무리를 지우므로, 잠깐 흔들린 네트워크는 다시 시도할 수 있습니다. 거부된 프로미스를 갈무리해 두면 한 번의 흔들림이 영영 남습니다.
  4. 동시에 부른 singleFlight는 진행 중인 프로미스를 함께 씁니다. 그래서 N개가 다투어도 fn은 한 번만 돕니다. "init()이 void를 돌려주니 부르는 쪽이 기다릴 수 없고, 그래서 이른 쓰기가 실패한다"는 고전적인 버그의 해답이기도 합니다.

0.3에서 이름을 바꿨습니다

memoize는 오해를 부르는 이름이었습니다. 인자별로 갈무리한 적이 한 번도 없고, 그저 한 번만 돌렸을 뿐입니다. 이제는 once의 별칭이며 더는 권하지 않습니다. 타입 서명도 예전에는 틀렸습니다(인자를 넘겨주면서도 인자 없는 것으로 선언했지요). 이제는 fn에서 추론합니다.

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