JavaScript generator는 언제든지 실행을 일시 중지하고 재개할 수 있는 기능으로, 시간이 지남에 따라 일련의 값을 생성할 수 있습니다.
그러므로 가장 큰 소수 찾기, 미로 통과 하기 같은 수학적 문제를 푸는데 유용합니다.
이 기능을 사용하면 계산을 더 작고 관리하기 쉬운 청크로 분할하여 병렬로 또는 한 번에 하나씩 실행할 수 있습니다. 이는 대량 데이터에대한 머신러닝 알고리즘을 푸는 대량의 데이터를 처리해야 하는 상황에서 유용할 수 있습니다.
JavaScript generator를 사용하려면 먼저 function*
구문을 사용하여 생성기 함수를 '정의'해야 합니다. 이 함수는 계산 또는 데이터 처리를 위한 코드를 포함하며, yield
키워드를 사용하여 일시 중지했다가 다시 시작할 수 있습니다.
다음은 간단한 제너레이터 함수의 예입니다.
function* myGenerator() {
// perform some initial computation
let result = computeInitialValue();
// yield the first result
yield result;
// perform some additional computation
result = computeNextValue(result);
// yield the next result
yield result;
// continue performing computation and yielding results until finished
while (result !== finalValue) {
result = computeNextValue(result);
yield result;
}
}
제너레이터 함수를 정의했으면 제너레이터 객체에서 next()
메서드를 호출하여 이 함수를 사용할 수 있습니다.
이렇게 하면 첫 번째 yield
키워드가 발견될 때까지 생성기 함수에서 코드가 실행되며, 이 시점에서 실행을 일시 중지하고 산출된 값(yielded value)을 반환합니다. 그런 다음 next()
를 다시 호출하여 일시 중지된 지점에서 실행을 다시 시작하고 다음으로 산출된 값(yielded value)을 생성할 수 있습니다. 다음은 위에서 정의한 myGenerator 기능을 사용하는 방법의 예입니다.
// create a generator object
const generator = myGenerator();
// get the first yielded value
const result1 = generator.next();
// get the second yielded value
const result2 = generator.next();
// continue getting yielded values until the generator is finished
while (!result.done) {
const result = generator.next();
// process the yielded value
processValue(result.value);
}
이와 같은 루프에서 next()
메서드를 호출하면 산출된 값을 한 번에 하나씩 처리할 수 있으므로 자바스크립트 프로그램의 메인 스레드를 차단하지 않고 장기 실행 계산을 수행하거나 대량의 데이터를 처리할 수 있습니다.
그렇습니다, javascript genenrator는 본질적으로 비동기적입니다. generator는 일시 중지했다가 다시 시작할 수 있는 특수한 유형의 기능으로, 시간이 지남에 따라 여러 값을 생성할 수 있습니다.
이를 통해 promise
이나 async/await
로 작업할 때와 같은 비동기 프로그래밍에 generator를 사용할 수 있습니다.
async function generateValues() {
yield 1;
yield 2;
yield 3;
}
async function consumeValues() {
const generator = generateValues();
const firstValue = await generator.next(); // { value: 1, done: false }
const secondValue = await generator.next(); // { value: 2, done: false }
const thirdValue = await generator.next(); // { value: 3, done: false }
}
위의 코드에서 consumeValues()
함수는 await
키워드를 사용하여 실행을 일시 중지하고 generator가 각 값을 반환할 때까지 기다립니다. 이는 asynchronous이고 non-blocking적인 상태에서 코드를 더 synchronous으로 보이는 스타일로 작성할 수 있게 해줍니다.
generator를 사용하여 재귀 함수를 생성하려면 yield*
키워드를 사용하여 generator 함수 내에서 재귀적으로 generator 함수를 호출하는 방식으로 구현할 수 있습니다.
이를 통해 generator 함수는 각 재귀 호출에서 실행을 일시 중지하고 다시 시작할 수 있으며, 시간이 지남에 따라 일련의 값을 생성할 수 있습니다.
다음은 재귀 함수를 구현하는 방법의 예입니다.
function* myGenerator(n) {
if (n <= 0) {
// base case: return the initial value
return 0;
} else {
// recursive case: yield the current value and call the generator recursively
yield n;
yield* myGenerator(n - 1);
}
}
이 예제에서, myGenerator
함수 작업은 argument로 받은 n부터 0까지 값을 생성하는 계산을 재귀적으로 수행합니다. 이 함수를 사용하려면 next() 메서드를 호출하여 한 번에 하나씩 값을 return 받아 처리할 수 있습니다.
다음은 myGenerator 기능을 사용하는 방법의 예입니다.
// continue getting yielded values until the generator is finished
for (const result of myGenerator(5)) {
doSomethingWithResult(result);
}
이러한 방식으로 JavaScript 생성기를 사용하면 언제든지 실행을 일시 중지했다가 다시 시작할 수 있는 방식으로 재귀 계산을 수행할 수 있으므로 오래 실행되거나 복잡한 계산을 더 쉽게 관리할 수 있습니다.
아래의 DFS/BFS 코드는 각 노드와 이웃 노드가 iterable하다고(예: 배열) 가정합니다.
function* dfs(node, visited = new Set()) {
// Mark the current node as visited
visited.add(node);
// Yield the current node
yield node;
// For each of the unvisited neighbors of the current node,
// run DFS on the neighbor
for (const neighbor of node.neighbors) {
if (!visited.has(neighbor)) {
yield* dfs(neighbor, visited);
}
}
}
위 DFS 예제는 그래프의 각 노드가 인접 노드에서 방문할 수 있는 인접 노드 속성을 가지고 있다고 가정합니다.
visited
는 set
으로 이미 방문한 노드를 추적하는 데 사용되며 방문한 노드를 다시 방문하지 않도록 합니다.
dfs generator 함수를 사용하기 위해서 generator 함수를 호출하고 반복되어 생성되는 값을 받을 수 있습니다.
const startingNode = getStartingNode();
for (const node of dfs(startingNode)) {
// Do something with each visited node
}
DFS를 구현한 것처럼 BFS도 Set을 활용한 visited
로 이미 방문한 노드를 추적하는 데 사용되므로 노드를 다시 방문하지 않습니다.
function* bfs(node, visited = new Set()) {
// Create a queue to store the nodes we need to visit
const queue = [node];
// While there are nodes in the queue, visit the next one
while (queue.length > 0) {
// Get the next node in the queue
const current = queue.shift();
// Mark the current node as visited
visited.add(current);
// Yield the current node
yield current;
// Add the unvisited neighbors of the current node to the queue
for (const neighbor of current.neighbors) {
if (!visited.has(neighbor)) {
queue.push(neighbor);
}
}
}
}
const startingNode = getStartingNode();
for (const node of bfs(startingNode)) {
// Do something with each visited node
}