Languages/JavaScript

[HUFS/GnuVil] #21 Set과 Map, 비동기 프로그래밍

성중 2022. 11. 18. 02:47

Set

Set🔽

 

Set - JavaScript | MDN

Set 객체는 자료형에 관계 없이 원시 값과 객체 참조 모두 유일한 값을 저장할 수 있습니다.

developer.mozilla.org

 

Set 객체는 중복되지 않는 유일한 값들의 집합으로, 배열과 다음과 같은 차이가 있다

 

배열과의 차이 (순서는 지켜진다)

const set1 = new Set([1, 2, 3, 3]);
console.log(set1); // Set(3) {1, 2, 3}

const set2 = new Set('hello');
console.log(set2); // Set(4) {"h", "e", "l", "o"}

이터러블을 인수로 전달받아 Set 객체를 생성할 수 있다. 이 때 중복 값은 제거된다

 

const set = new Set();

set.add(1).add(2).add(2);
console.log(set); // Set(2) {1, 2}

중복된 요소의 추가는 에러가 발생하지 않고 무시된다

 

const set = new Set([1, 2, 3]);

// 존재하지 않는 요소 0을 삭제하면 에러없이 무시된다.
set.delete(0);
console.log(set); // Set(3) {1, 2, 3}

존재하지 않는 값의 삭제도 마찬가지로 에러 없이 무시된다

 

const set = new Set([1, 2, 3]);

// Set 객체는 Set.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in set); // true

// 이터러블인 Set 객체는 for...of 문으로 순회할 수 있다.
for (const value of set) {
  console.log(value); // 1 2 3
}

// 이터러블인 Set 객체는 스프레드 문법의 대상이 될 수 있다.
console.log([...set]); // [1, 2, 3]

// 이터러블인 Set 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
const [a, ...rest] = [...set];
console.log(a, rest); // 1, [2, 3]

이터러블이므로 for…of 문으로 순회 및 스프레드 문법으로 펼쳐 활용할 수 있다

* Set 객체를 활용해 교집합/합집합/차집합/부분집합 등을 구현

 

Map

Map🔽

 

Map - JavaScript | MDN

The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.

developer.mozilla.org

 

Map 객체는 키와 값의 쌍으로 이루어진 컬렉션으로, 객체와 유사하나 다음과 같은 차이가 있다

* 스프레드로 펼치거나 keys/values/entries 메서드, forEach, for…of 문 등을 활용해 순회

 

일반 객체와의 차이

const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map1); // Map(2) {"key1" => "value1", "key2" => "value2"}

const map2 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object

Map 객체를 생성할 때 인수를 전달한다면 키와 값의 쌍으로 이루어져야 한다

 

const map = new Map();

map
  .set('key1', 'value1')
  .set('key1', 'value2');

console.log(map); // Map(1) {"key1" => "value2"}

중복된 키를 갖는 요소를 추가하면 값이 덮어씌워지며 에러는 발생하지 않는다

 

const map = new Map();

const lee = { name: 'Lee' };
const kim = { name: 'Kim' };

// 객체도 키로 사용할 수 있다.
map
  .set(lee, 'developer')
  .set(kim, 'designer');

console.log(map);
// Map(2) { {name: "Lee"} => "developer", {name: "Kim"} => "designer" }

일반 객체와 달리 키의 타입에 제한이 없어 객체를 포함한 모든 값을 키로 사용할 수 있다

 

비동기 프로그래밍

함수를 호출하면 함수 코드가 평가되어 실행 컨텍스트 스택에 쌓이고 실행되며 제거된다

 

실행 컨텍스트 스택

자바스크립트 엔진은 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작하기 때문에 단 하나의 실행 컨텍스트 스택을 가지며 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹(작업 중단)이 발생한다

 

// sleep 함수는 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다.
function sleep(func, delay) {
  // Date.now()는 현재 시간을 숫자(ms)로 반환한다.("30.2.1. Date.now" 참고)
  const delayUntil = Date.now() + delay;

  // 현재 시간(Date.now())에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복한다.
  while (Date.now() < delayUntil);
  // 일정 시간(delay)이 경과한 이후에 콜백 함수(func)를 호출한다.
  func();
}

function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
}

// sleep 함수는 3초 이상 실행된다..
sleep(foo, 3 * 1000);
// bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹된다.
bar();
// (3초 경과 후) foo 호출 -> bar 호출

setTimeout 함수로 일정 시간 이후 다음 함수가 호출되도록 했을 때, 그동안 다음 함수는 호출되지 못하고 블로킹(작업 중단)된다. 이처럼 현재 실행 중인 태스크가 종료될 때까지 다음 태스크가 대기하는 방식동기(synchronous) 처리라고 하며 실행 순서가 보장되는 대신 나머지 태스크들이 지연되는 단점이 있다

 

function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
}

// 타이머 함수 setTimeout은 일정 시간이 경과한 이후에 콜백 함수 foo를 호출한다.
// 타이머 함수 setTimeout은 bar 함수를 블로킹하지 않는다.
setTimeout(foo, 3 * 1000);
bar();
// bar 호출 -> (3초 경과 후) foo 호출

위 방식은 동일하게 setTimeout 함수를 사용하지만 태스크를 블로킹하지 않고 곧바로 실행한다. 이처럼 현재 태스크가 실행 중이라도 다음 태스크를 곧바로 실행하는 방식비동기(asynchronous) 처리라고 하며 실행 순서가 보장되지 않는 단점이 있다

* 기본적으로 타이머 함수와 HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작하며, 이를 보완하기 위해 전통적으로 콜백 패턴을 사용했지만 중첩되면 가독성이 떨어지며 에러의 예외 처리가 곤란한 문제가 존재

 

비동기 처리에서 소스 코드의 평가/실행을 제외한 모든 처리는 브라우저Node.js가 담당한다

  • 콜 스택: 소스코드 평가 과정에서 생성된 실행 컨텍스트가 관리되는 스택 자료구조
  • 힙: 객체가 저장되는 메모리 공간으로, 콜 스택의 실행 컨텍스트가 힙을 참조
  • 태스크 큐: 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 실행 전 일시적으로 보관되는 영역
  • 이벤트 루프: 콜 스택의 실행 중인 컨텍스트가 있는지, 태스크 큐에 대기 중인 함수가 있는지 반복적으로 확인해 만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택로 이동 (= 비동기 처리 방식으로 동작)

이벤트 루프와 브라우저 환경

자바스크립트는 분명 한 번에 하나의 태스크만 처리하는 싱글 스레드로 동작하지만 브라우저는 많은 태스크가 동시에 처리되는 것처럼 느껴진다. 이는 브라우저에 자바스크립트의 동시성(concurrency)을 보장하는 이벤트 루프(Event loop)가 내장되어 있기 때문이다

* 자바스크립트 엔진 = 싱글 스레드 / 브라우저 = 멀티 스레드

 

 내용은 위키북스의 '모던 자바스크립트 Deep Dive' 바탕으로 작성되었습니다.