동기 api가 구현은 편하지만 항상 그럴수는 없다. 그렇기에 비동기적으로 초기화 작업을 구현하는 방법을 알아보자.
아래는 비동기적으로 초기화된 컴포넌트의 일반적인 예이다.
예시 (db.js)
import { EventEmitter } from 'events'
class DB extends EventEmitter {
connected = false
connect () {
// 연결 지연 시뮬레이션
setTimeout(() => {
this.connected = true
this.emit('connected')
}, 500)
}
async query (queryString) {
if (!this.connected) {
throw new Error('Not connected yet')
}
console.log(`Query executed: ${queryString}`)
}
}
export const db = new DB()
위의 db모듈은 초기화가 완료될때까지 다른 작업을 할 수 없다.
이 문제에 대한 두가지 해결책
1) 로컬 초기화
2) 지연 시작
API가 호출되기 전에 모듈이 초기화 되었는지 확인 그렇지 않으면 초기화되기를 기다린다.
import { once } from 'events'
import { db } from './db.js'
db.connect()
async function updateLastAccess () {
if (!db.connected) {
await once(db, 'connected') // 해당 이벤트 발생 시까지 실행 중단.
}
await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`)
}
updateLastAccess()
setTimeout(() => {
updateLastAccess()
}, 600)
컴포넌트가 초기화 루틴을 완료할 때까지 비동기적으로 초기화된 컴포넌트에 의존하는 코드의 실행을 지연시키는 것
import { once } from 'events'
import { db } from './db.js'
async function initialize () {
db.connect()
await once(db, 'connected')
}
async function updateLastAccess () {
await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`)
}
initialize()
.then(() => {
updateLastAccess()
setTimeout(() => {
updateLastAccess()
}, 600)
})
단점 : 비동기적으로 초기화되어야 하는 컴포넌트를 사용하는 컴포넌트가 어떤 것인지 미리 알아야한다.
비동기 초기화 단계가 완료될 때까지 모든 작업을 투명하고 효율적으로 지연시킬수있는 사전 초기화 큐
컴포넌트가 아직 초기화되지 않은 상태에서 수신된 함수 호출을 큐에 넣은 다음, 모든 초기화 단계가 완료되는 즉시 실행하는 것이다.
import { EventEmitter } from 'events'
class DB extends EventEmitter {
connected = false
commandsQueue = []
async query (queryString) {
if (!this.connected) {
console.log(`Request queued: ${queryString}`)
return new Promise((resolve, reject) => {
const command = () => {
this.query(queryString)
.then(resolve, reject)
}
this.commandsQueue.push(command)
})
}
console.log(`Query executed: ${queryString}`)
}
connect () {
setTimeout(() => {
this.connected = true
this.emit('connected')
this.commandsQueue.forEach(command => command())
this.commandsQueue = []
}, 500)
}
}
export const db = new DB()
비동기 작업을 처리할때 동일한 API에 대한 일련의 호출을 일괄처리하여 가장 기본적인 수준의 캐싱을 수행할수 있다.
요청이 완료되자마자 그 결과를 캐시에 저장한다. 따라서 다음에 API가 호출될때 다른 요청을 생성하는 대신 캐시에서 즉시 결과를 검색할 수 있다.
참고
Node.js 디자인 패턴 바이블 - 영진닷컴