글 목록
- JavaScript의 using을 사용해 보자 - Part 1
- JavaScript의 using을 사용해 보자 - Part 2
Intro
지난 글에서 예고한 대로 TypeScript를 이용해,
using
을 명시적으로 사용해 보겠습니다.
리소스를 관리하는 방식을 using
을 이용하여 작성해 보겠습니다.
동기에 대한 명시적 선언
다음은 명시적으로 Symbol.dispose
를 이용하여,
동기 코드에 대한 종료 함수를 선언하는 방법입니다.
using
이 선언된 블록이 종료되면,
명시된 Symbol.dispose
에 할당된 함수를 실행합니다.
{
const getResource = () => {
return {
[Symbol.dispose]: () => {
console.log("using 종료.")
},
}
}
using fileHandler = getResource()
}
// 종료 후, [Symbol.dispose] 실행.
// using 종료. 출력
비동기에 대한 명시적 선언
다음은 명시적으로 Symbol.asyncDispose
를 이용하여,
비동기 코드에 대한 종료 함수를 선언하는 방법입니다.
top-level에서 await
를 사용하는 모습이 약간은 어색해 보입니다.
const getResource = () => ({
[Symbol.asyncDispose]: async () => {
await asyncSomethingFunc()
},
})
{
await using fileHandler = getResource()
}
1편으로 돌아가보기
1편에서 우리는 데이터베이스 연결에 대한 이야기를 나누어 보았습니다.
지금까지 using
에 대해서 이야기한 것을 바탕으로 using
을 이용하여
데이터베이스 연결을 관리해 보겠습니다.
getPosts 함수 내부 들여다 보기
using
을 쓰지 않는 경우
let db
try {
db = await connectToDatabase()
let collection = await db.collection("posts")
let posts = await collection.find({}).limit(50).toArray()
return posts
} catch (error) {
console.error(error)
} finally {
await db.close()
}
using
을 쓰는 경우
const getConnection = async () => {
const connection = await connectToDatabase()
return {
connection,
[Symbol.asyncDispose]: async () => {
await connection.close()
},
}
}
{
await using db = await getConnection()
let collection = await db.collection("posts")
let posts = await collection.find({}).limit(50).toArray()
return posts
}
위 코드를 보면 try...catch...finally
로 연결되는 블록을 using
을 활용해서
명시적으로 컨트롤할 수 있게 변경된 것을 볼 수 있습니다.
실제 폴리필은 어떻게 이루어질까?
폴리필
단계를 분리하여 함수별로 확인해 보겠습니다.
크게 3가지의 단계로 진행됩니다.
- 이미 정의된
Symbol
객체 disposableResource
의 관리- 명시된
disposeResource
의 사용 및 해제
Symbol
정의
현재 dispose
, asyncDispose
로 정의된 Symbol
은 core-js
에 있는 정의를
활용합니다.
해당 파일은 WellKnownSymbol로 dispose
, asyncDispose
를 선언합니다.
import "core-js/modules/esnext.symbol.dispose.js"
import "core-js/modules/esnext.symbol.async-dispose.js"
// WellKnownSymbol로 `dispose`, 'asyncDispose`를 선언합니다.
// core-js/moduekls/esnext.symbol.dispose.js
defineWellKnownSymbol("dispose")
// core-js/moduekls/esnext.symbol.async-dispose.js
defineWellKnownSymbol("asyncDispose")
disposable-stack
을 활용하기
이러한 명시적 관리는 disposable-stack
으로 명명된 stack을 통해 내부에서
관리하고 있습니다.
실제 내부 코드는 제외하고 사용하는 함수를 표기하겠습니다.
import "core-js/modules/esnext.disposable-stack.constructor.js"
defineBuiltIns(DisposableStackPrototype, {
// Dispose Resource 객체를 생성합니다.
dispose: function dispose() {},
// Stack에 담겨 있는 동기 Dispose Resource 객체를 꺼내와 dispose method를 실행합니다.
use: function use(value) {},
// Stack에 담겨 있는 비동기 Dispose Resource 객체를 꺼내와 dispose method를 실행합니다.
adopt: function adopt(value, onDispose) {},
// Stack에 담겨 있는 비동기 Dispose Resource 객체를 골라 지연합니다.
defer: function defer(onDispose) {},
// 해당 Stack의 instance가 다 비워지면 다음 Stack을 찾아 이동합니다. Stack은 블록 단위로 구성됩니다.
move: function move() {},
})
실제 TypeScript에서 폴리필을 구성해 주는 코드입니다.
var __addDisposableResource =
(this && this.__addDisposableResource) ||
function (env, value, async) {
...생략...
// built-in으로 되어있는 DisposableResource를 등록합니다.
return value
}
var __disposeResources =
(this && this.__disposeResources) ||
(function (SuppressedError) {
return function (env) {
// built-in으로 되어있는 disposeResource 함수를 실행합니다.
}
})(
// Error 전달
SuppressedError
)
Outro
TypeScript5.2에서 폴리필을 구성해서 실제 core-js
에 구현된 부분까지
훑어보았습니다.
using
을 아직 한 번도 사용해 본 적이 없거나,
using
을 막 도입하여 패턴을 변경하고 싶은 분들에게 필요한 내용들이 되었으면
합니다.
내일은 마침 추석 연휴가 시작되는 날입니다. 모두 즐거운 한가위 되셨으면 합니다.