JavaScript 특징

Promise 병렬 처리하는 방법을 알아보기 전에 JavaScript의 두 가지 특성부터 확인해 본다

싱글 스레드(single-thread)

JavaScript는 싱글 스레드(Single-Thread) 방식이므로 명령어 또는 코드가 순차적으로 실행됩니다. 그러므로 동시에 2개 이상의 함수를 실행시킬 수 없습니다.

이벤트 루프(Event Loop)

JavaScript는 싱글 스레드 방식으로 동작하므로 이론적으로 비동기 작업이 불가능합니다. 하지만, 싱글 스레드가 이벤트 루프에 의해 처리되므로 멀티 스레딩(Multi-Thread) 방식으로 동작하는 것처럼 보여줍니다.

이벤트 루프의 실행 에뮬레이터 http://latentflip.com/loupe

Promise

Promise는 비동기 처리에 사용되는 객체입니다. JavaScript 특성인 이벤트 루프의 규범을 따르지 않고 비동기 이벤트로 분류됩니다.

이벤트 루프는 Promise를 발견하면 비동기로 동작하는 기능(메서드, 함수)들이 이벤트 루프에서 분리됩니다. 비동기로 동작하는 기능들은 Promise가 이행(fulfilled)될 때까지 대기열(Queue)에 배치됩니다.

버튼을 클릭하면 onClick() 함수가 호출된다고 가정합시다. onClick() 함수에는 두 개의 비동기 호출이 존재합니다.

onClick = async () => {
  try {
    console.time("ABC");
    const data001 = await new Promise((resolve) =>
      setTimeout(resolve, 3000, "promise001")
    );

    const data002 = await new Promise((resolve) =>
      setTimeout(resolve, 5000, "promise002")
    );
    console.timeEnd("ABC");
  } catch (error) {
    console.error(error);
  }
};

실행 결과

ABC: 8004ms

표준 async/await 구문을 사용하여 두 개의 Promise를 처리하면, 코드가 순차적으로 처리되므로 첫 번째 Promise가 해결될 때까지 두 번째 Promise는 실행되지 않습니다.

첫 번째 Promise는 3초, 두 번째 Promise는 5초 걸릴 것으로 예상됩니다. console.time()을 사용하여 onClick() 함수의 실행시간을 측정하면 대략 8초입니다.

Promise.all()

Promise.all() 메서드는 인수로 해결해야 하는 모든 Promise를 가지며, 단일 Promise를 반환합니다.

onClick = async () => {
  try {
    console.time("ABC");
    const data001 = new Promise((resolve) =>
      setTimeout(resolve, 3000, "promise001")
    );

    const data002 = new Promise((resolve) =>
      setTimeout(resolve, 5000, "promise002")
    );

    const data003 = await Promise.all([data001, data002]);
    console.log(data003);
    console.timeEnd("ABC");
  } catch (error) {
    console.error(error);
  }
};

실행 결과

(2) ["promise001", "promise002"]
ABC: 5004.5ms

첫 번째 Promise와 두 번째 Promise 앞에 await 키워드를 제거 후 Promise가 할당된 객체인 data001과 data002를 Promise.all() 메서드에 배열로 전달합니다.

Promise.all() 함수는 두 개의 비동기 요청이 해결될 때까지 기다립니다. 결과적으로 비동기 처리 시간이 감소되며, 두 개의 Promise가 멀티 스레드 방식으로 처리되는 것처럼 보이게 합니다.

Promise.all() 함수는 전달된 Promise에서 하나라도 실패하면, 즉시 거부(reject)합니다.

onClick = async () => {
  console.time("ABC");
  try {
    const data001 = new Promise((resolve) =>
      setTimeout(resolve, 3000, "promise001")
    );

    const data002 = Promise.reject("promise002 reject");

    const data003 = await Promise.all([data001, data002]);
    console.log(data003);
  } catch (error) {
    console.error(error);
  } finally {
    console.timeEnd("ABC");
  }
};

실행 결과

TypeError: undefined is not a promise
ABC: 3.100000001490116ms

data002가 거부되었으므로 data001을 처리하지 않고 catch문으로 바로 이동합니다.

Promise.allSettled()

Promise.allSettled() 함수는 Promise.all() 함수와 동일한 인수를 가지며, 단일 Promise를 반환합니다.

Promise.allSettled() 함수의 특징은 전달된 Promise에서 거부(reject)되었는지 여부에 관계없이 해결된 Promise를 반환한다는 것입니다.

반환된 Promise는 이행된(fulfilled) 상태와 거부(rejected) 상태를 가집니다.

onClick = async () => {
  console.time("ABC");
  try {
    const data001 = new Promise((resolve) =>
      setTimeout(resolve, 3000, "promise001")
    );

    const data002 = Promise.reject("promise002 reject");

    const data003 = await Promise.allSettled([data001, data002]);
    console.log(data003);
  } catch (error) {
    console.error(error);
  } finally {
    console.timeEnd("ABC");
  }
};

실행 결과


정리

  • Promise.all() 함수와 Promise.allSettled() 함수로 Promise를 병렬 처리할 수 있습니다.
  • 모든 Promise가 정상적으로 이행(fulfilled)되어야 하는 경우 Promise.all() 함수를 호출합니다.
  • Promise 거부와 상관없이 병렬로 처리해야 하는 경우 Promise.allSettled() 함수를 호출합니다.

The logical && (AND) operator

const trueCondition = true;
const falseCondition = false;

const obj = {
  ...(trueCondition && { dogs: "woof" }),
  ...(falseCondition && { cats: "meow" }),
};

// { dogs: 'woof' }

const arr = [
  ...(trueCondition ? ["dog"] : []),
  ...(falseCondition ? ["cat"] : [])
];

// ['dog']

The spread operator (...)

let myDogs = [`Riggins`, `Lyla`];
let parentsDogs = [`Ellie`, `Remi`];

const holidayDoghouse = [...myDogs, ...parentsDogs];
// [ 'Riggins', 'Lyla', 'Ellie', 'Remi' ]
let existingAnimals = {
  dogs: 2,
  cats: 4,
  donkeys: 2,
  horses: 2,
};

let newAnimals = {
  goats: 2,
};

const allAnimals = {
  ...existingAnimals,
  ...newAnimals,
};
// { dogs: 2, cats: 4, donkeys: 2, horses: 2, goats: 2 }

 

'Javascript' 카테고리의 다른 글

[Javascript] Promise  (1) 2022.11.17
Javascript - console.log()  (0) 2022.04.07
[Javascript] 정렬 함수 sort  (0) 2021.12.17
[JavaScript] Object Literals (정리)  (0) 2021.07.14
[Javascript] 함수 표현식 vs 함수 선언식  (0) 2019.08.05

기본 출력문 - log, info, debug, warn, error

console.log('기본');
console.info('정보');
console.debug('디버그');
console.warn('경고');
console.error('에러');

 console로 출력할 수 있는 출력문은 level(중요도)로 분류해서 사용할 수 있습니다. log4j처럼

console의 level [log, info, debug, warn, error]

 만약 console.debug()로 출력한 내용이 나오지 않으면 콘솔에 출력 옵션을 확인해보시면 됩니다.

크롬의 console level 설정

 크롬 같은 경우 console 출력 설정 기본 값이 Info, Warnings, Errors라 Debug는 출력이 안될 수 있기 때문에 옵션이 원하는 데로 설정이 되었는지 확인이 필요합니다.

여러 가지 출력 방법

const owner = 'YD';
const message = 'Hello world!';
const isDeveloper = true;
const age = 27;

console.log(owner); // 하나의 값만 출력
console.log(owner, message, isDeveloper); // 여러 개의 값을 동시에 출력
console.log('%s는 문자와 %d는 숫자', owner, age); // 치환 문자 사용하기
console.log(`${owner} ${message}`); // "`" 백틱 사용하기

 console.log()는 단 하나의 값뿐만 아니라 여러 가지 방법을 이용해서 값을 출력할 수 있습니다. 개발자마다 취향은 다르지만 개인적으로 선호하는 방법은 백 틱을 사용하는 방법입니다. 사용하는 방법에는 정해진 가이드가 없으니 편한 방법을 택해서 사용하시면 됩니다.

스타일을 입혀보자

// 출력문에 스타일 적용하기
console.log('%c출력문의 색상과 글자 크기를 변경하였습니다.', 'color: green; font-size: 16px;');

// 여러가지 스타일을 동시에 적용하고, 추가 메세지 출력하기
console.log('다중 스타일과 추가 메세지 출력하기: %c초록 %c파랑', 'color: green', 'color: blue', '추가 메세지 작성');

 console.log()는 %c(치환 문자)를 사용하면 스타일을 설정할 수 있습니다. 뿐만 아니라 동시에 여러 개의 스타일을 적용할 수도 있습니다.

console.log()에 스타일 적용하기

 

 

 

객체 출력하기

const obj = { owner: 'YD', text: 'Hello world!' };

console.log(obj); // 객체를 출력할 때는 이 방법 대신
console.log(JSON.parse(JSON.stringify(obj))); // 이 방법을 사용하자!

 이 내용은 이전 글에서도 다룬 내용이지만 구체적인 이유는 다루지 않았기 때문에 한번 더 이야기해보도록 하겠습니다. 왜 객체를 출력할 때는 JSON.parse()와 JSON.stringify() 함수까지 번거롭게 사용하면서 객체를 출력해야 하는 걸까요? 그 이유는 console.log()가 우리가 원하는 방식대로 동작하지 않기 때문입니다. console.log()를 통해 객체를 출력하면 console.log()를 사용한 시점의 객체가 출력되길 원하지만 결과는 그렇지 않기 때문입니다.

let obj = {};
console.log(obj);
// 출력되길 원하는 값: {}

obj.owner = 'YD';
console.log(obj);
// 출력되길 원하는 값: { owner: 'YD' }

 위의 예제 코드를 출력해보면 아래와 같은 결과를 확인할 수 있습니다. 2번째 줄 console.log()에서는 어떤 값도 가지지 않고 있어야 하는데 코드를 실행하면 obj가 값을 가지고 있다고 보이고 있습니다. 많은 브라우저들이 객체의 경우에는 값이 경신될 때마다 바뀐 값을 보여주게 되어있습니다. 그렇기 때문에 객체를 출력할 때는 console.log(JSON.parse(JSON.stringify(obj))) 이렇게 사용하는 것이 가장 좋습니다.

cosole.log()를 사용한 객체 출력

DOM 요소를 JSON처럼 보여주는 console.dir()

 console에는 log()와 같아 보이지만 다른 출력문 기능인 dir()이 있습니다. log()와 dir() 차이는 요소를 HTML과 같은 트리 구조를 보여주느냐 JSON과 같은 형태로 구성 요소를 보여주느냐입니다.

 

  Element가 가진 메서드를 확인하고 싶어서 console.log()를 사용해보신 적이 있으실 텐데요. 원하는 결과는 보여주지 않고 HTML의 구조만 보여줍니다. 바로 이럴 때 console.dir()를 사용하시면 됩니다.

console.log()와 console.dir()의 차이

 

 

 

console.log()를 묶어서 사용해보자

console.group();
    console.log('Hello world!');
    console.group('Web Developer');
        console.log('Frontend Engineer');
        console.log('YD');
    console.groupEnd();
console.groupEnd();

 console을 이용해 메시지를 그룹화시켜서 사용할 수 있습니다. 이는 시각적으로 계층을 분리하는 효과가 있으며, log level과 같이 사용하면 활용도를 더 높일 수 있습니다.

console.group()를 사용하면 가질 수 있는 시각적 혜택

배열을 멋지게 보는 console.table()

let data = ['Mark', 'YD', 'Evan'];
console.table(data);

 배열 형식의 데이터를 console.table()를 사용하면 console.log() 보다 멋진 출력물을 확인할 수 있습니다. JSON 형태의 값도 확인할 수 있습니다.

console.table() 출력 결과

숫자를 세어보자 console.count()

console.count('Yap!');
console.count('Yap!');
console.count('Yap!');
console.count('Yap!');

 이 함수는 특정 count()가 호출된 횟수를 기록하고 보여줍니다. label을 옵션으로 사용하며 횟수를 기록합니다. console.countReset()를 호출하면 누적된 기록이 초기화됩니다.

console.count() 사용 결과

성능을 측정해볼까? console.time()과 console.timeEnd()

console.time('타이머');
let value = 0;
for (let count = 0; count < 100; count++) {
    value += 5;
}
console.timeEnd('타이머');

 코드의 소요 시간을 측정할 때 사용하면 유용한 기능입니다. label을 필수 인자로 사용하며, 타이머를 식별하는데 이용됩니다. 시간 단위는 밀리초를 사용합니다.

console.time()과 console.timeEnd()를 사용한 소요 시간 측정 결과

자바스크립트 배열의 내장 함수에 sort()가 있다. 명칭 그대로 배열 안의 원소를 정렬하는 함수이다. 추후에 또 검색할 것 같아서 적어놓는다.

arrayobj.sort(sortFunction)

 
arrayobj는 임의의 Array 개체이다. sortFunction는 요소 순서를 결정하는 데 사용되는 함수의 이름이다.

생략하면 오름차순, ASCII 문자 순서로 정렬된다.

sortFunction 인수에 함수를 지정하면 아래의 값 중 하나가 반환된다.

 

  • 첫 번째 인수가 두 번째 인수보다 작을 경우 - 값
  • 두 인수가 같을 경우 0
  • 첫 번째 인수가 두 번째 인수보다 클 경우 + 값
  • 정렬할 Array의 요소의 개수가 2개 미만일 경우 ‘sort is not a function’ 오류가 난다.

문자 정렬

var fruit = ['orange', 'apple', 'banana'];

/* 일반적인 방법 */
fruit.sort(); // apple, banana, orange

숫자 정렬

var score = [4, 11, 2, 10, 3, 1]; 

/* 오류 */
score.sort(); // 1, 10, 11, 2, 3, 4 
              // ASCII 문자 순서로 정렬되어 숫자의 크기대로 나오지 않음

/* 정상 동작 */
score.sort(function(a, b) { // 오름차순
    return a - b;
    // 1, 2, 3, 4, 10, 11
});

score.sort(function(a, b) { // 내림차순
    return b - a;
    // 11, 10, 4, 3, 2, 1
});

Object 정렬

var student = [
    { name : "재석", age : 21},
    { name : "광희", age : 25},
    { name : "형돈", age : 13},
    { name : "명수", age : 44}
]

/* 이름순으로 정렬 */
student.sort(function(a, b) { // 오름차순
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
    // 광희, 명수, 재석, 형돈
});

student.sort(function(a, b) { // 내림차순
    return a.name > b.name ? -1 : a.name < b.name ? 1 : 0;
    // 형돈, 재석, 명수, 광희
});

/* 나이순으로 정렬 */
var sortingField = "age";

student.sort(function(a, b) { // 오름차순
    return a[sortingField] - b[sortingField];
    // 13, 21, 25, 44
});

student.sort(function(a, b) { // 내림차순
    return b[sortingField] - a[sortingField];
    // 44, 25, 21, 13
});

정리

우선 [20, 10, 9,8,7,6,5,4,3,2,1]의  배열이 있을경우 배열에서 a-b라는 연산을 모두 한 다음
그 결과값으로 정렬하는 것이 아니라

 

( a, b ) 형식으로 지정한 두 인자를 차례로 비교한다.

배열 a[0]과 b[1] 즉, 20과 10을 비교 20-10 = 10
결과값이 10 즉, 양수


sort함수에 sortNumber(a,b)의 return 값으로 양수 10을 전달합니다.
그럼 sort함수가 양수값을 전달받고 배열의 순서를 바꾸어 버립니다.
(정확하게 말하면 두 배열 안에 든 값을 교체)


그럼 배열이 [10, 20, 9,8,7,6,5,4,3,2,1] 이렇게 바뀝니다.

그 다음 a[0]과 b[2] 즉 10과 9를 비교합니다. 10 - 9 = 1 >0, 양수입니다.
결과값이 양수이므로 또 10과 9의 순서를 바꿉니다.


이런 식으로 계속 두 인자를 비교해서 결과값이 양수가 나오면 순서를 바꾸고,
음수가 나오면 순서를 그대로 유지.

 

이해하기 쉽게 배열이 바뀌어가는 순서를 보면

[(20), (10), 9,8,7,6,5,4,3,2,1] 20-10 = 10, 즉 양수이므로 순서바뀜! ()는 비교되는 인자값.
[(10), 20, (9),8,7,6,5,4,3,2,1] 10 - 9 = 1 또 양수, 순서 바뀜.
[(9), 20, 10, (8),7,6,5,4,3,2,1] 반복...
[(8), 20, 10, 9,(7)...]
...
[(2). 20, 10...3, (1)]
[(1), 20, 10...]

그럼 배열 내에서 가장 작은 값 1이 찾게되고

[1, 20, 10, 9,8,7,6,5,4,3,2]

1의 순서는 바뀌지 않습니다. 1-2 = -1
즉 결과값이 음수이기 때문.

 

그 다음은 두번째...
20 - 10 = 10 > 0 이므로 순서를 또 바꿉니다.

[1, (20), (10), 9,8,7,6,5,4,3,2]
[1, (10), 20, (9), 8...]
[1, (9), 20, 10, (8)...]

이런 식으로 반복하다 보면 두번째로 작은 값 2도 찾게 됨

....
[1, 2, 20, 10, 9,8,7,6,5,4,3]

 

그럼 다음은 세번째...

이렇게 순차적으로 반복하면 결국 정렬이 진행이 된다고 생각하면 된다.

자바스크립트에서 한 쌍의 중괄호 {} 로 묶어 리터럴 표기법을 사용하여 단일 객체를 생성 할 수 있다.

아래 코드는 객체 color 생성과 동시에  red, black 속성과 해당 값을 추가

 

//color 객체 생성
var color = { 
   red : "#ff0000"
  ,black : "#000000"
};
console.log(color.red); // >> #ff0000
console.log(color.red); // >> #000000

생성과 동시에 할당된 color 객체의 속성의 값을 [수정,추가,삭제] 작업을 수행할 수 있다.

즉 이미 할당된 객체의 값을  이동중에도 원하는대로  작업이 가능하다.

아래 코드는 생성된 color 단일 객체의 red, blue, print  속성 값을 제어 한다.

 

//color 객체 생성
var color = { 
   red : "#ff0000"
  ,black : "#000000"
};

//객체 속성  수정
color.red = "#8b0000";

//객체 속성  추가
color.blue = "#0000ff";

//객체 메서드 추가
color.print = function(){
  for (var prop in this){
	console.log( prop +":" + this[prop] );
  }
}

console.log(color.print());
// red:#8b0000
// black:#000000
// blue:#0000ff

 

자바스크립트에서 단일 객체의 속성 값은 기본 유형으로 제한되지 하지 않고 있기 때문에

다른 객체인 속성을 추가 하거나 함수(function) 또한 작성 할 수 있다.

 

단일 객체 리터럴 표기법은 필요한 모든 것이 단일 객체이고 여러 인스턴스가 필요하지 않은 경우 선호 되겠지만

인스턴스가 생성하고 미리 정의 된 값을 기반으로 작업이 필요한 경우 생성자 함수를 사용하는 작성 하는 것이 좋을 것이다.

 

 

+ Recent posts