프라미스 

promise 객체 기본 구조

let promise = new Promise(function(resolve, reject) {
  // executor (제작 코드)
});

  • executer 는 자동으로 실행, 이 부분에서 코드로 작성한 원하는 작업이 처리됩니다.
  • executer 의 인수 resolve 와 reject 는 자바스크립트에서 자제 제공하는 콜백입니다.
    • resolve : 작업이 성공적으로 끝난 경우 그 결과를 나타낸는 value 와 함께 호출됩니다.
    • reject : 에러 발생시 에러객체를 나타내는 error와 함께 호출
  • 프로미스의 특징 : 성공또는 실패 둘 중 하나만 존재

예시

let promise = new Promise(function(resolve, reject) {
  resolve("완료");

  reject(new Error("…")); // 무시됨
  setTimeout(() => resolve("…")); // 무시됨
});

 

프라미스 소비자  then, catch, finally

then

- 첫 번째 인수는 프라미스가 fullfilled 되었을 때 실행 -> 실행 결과

- 두 번째 인수는 프라미스가 refuse 되었을 때 실행 -> 에러 호출

promise.then(
  function(result) { /* 결과(result)를 다룹니다 */ },
  function(error) { /* 에러(error)를 다룹니다 */ }
);

 

reject 예시

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("에러 발생!")), 1000);
});

// reject 함수는 .then의 두 번째 함수를 실행합니다.
promise.then(
  result => alert(result), // 실행되지 않음
  error => alert(error) // 1초 후 "Error: 에러 발생!"을 출력
);

 

resolve 예시

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("완료!"), 1000);
});

// resolve 함수는 .then의 첫 번째 함수(인수)를 실행합니다.
promise.then(
  result => alert(result), // 1초 후 "완료!"를 출력
  error => alert(error) // 실행되지 않음
);

 

Catch

에러만 다루고 싶은 경우 사용 try {...} catch{...} 에서 catch 와 같은 역할

.then() 을 다 나열한 후 마지막에 사용되는 소비자

 

Finally

결과가 어떻든 마무리가 필요하면 finally 가 유용

finally 에선 절차를 마무리하는 보편적 동작을 수행하기 때문에  성공 혹은 실패 여부를 몰라도 됨

finally 는 자동으로 다음 핸들러에 결과와 에러를 전달

 

 

참고자료

https://ko.javascript.info/async

콜백

setTimeout은 스케줄링에 사용되는 가장 대표적인 함수

스크립트나 모듈을 로딩하는 것 또한 비동기 동작

 

콜백 기반(callback-based)’ 비동기 프로그래밍

<예시>

  • 스크립트 읽어오는 함수
function loadScript(src) {
  // <script> 태그를 만들고 페이지에 태그를 추가합니다.
  // 태그가 페이지에 추가되면 src에 있는 스크립트를 로딩하고 실행합니다.
  let script = document.createElement('script');
  script.src = src;
  document.head.append(script);
}
  • 스크립트 로딩이 끝나자마자 이 스크립트를 사용해 무언가를 해야만 한다고 가정
// script.js엔 "function newFunction() {…}"이 있습니다.
loadScript('/my/script.js'); 

newFunction(); // 함수가 존재하지 않는다는 에러가 발생합니다!

     - 에러는 브라우저가 스크립트를 읽어올 수 있는 시간을 충분히 확보하지 못했기 때문에 발생

  • loadScript 의 두 번째 인수로 스크립트 로딩이 끝난 후 실행될 함수인 콜백함수를 추가
function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(script);

  document.head.append(script);
}
  • 새롭게 불러온 스크립트에 있는 함수를 콜백 함수 안에서 호출
loadScript('/my/script.js', function() {
  // 콜백 함수는 스크립트 로드가 끝나면 실행됩니다.
  newFunction(); // 이제 함수 호출이 제대로 동작합니다.
  ...
});

→ 원하는 대로 외부 스크립트 안의 함수를 사용가능

 

무언가를 비동기적으로 수행하는 함수는 함수 내 동작이 모두 처리된 후 실행되어야 하는 함수가 들어갈 콜백을 인수로 반드시 제공해야 합니다. 이렇게 콜백을 사용한 방식은 비동기 프로그래밍의 일반적인 접근법

 

콜백 속 콜백

 

스크립트가 두 개 있는 경우, 어떻게 하면 두 스크립트를 순차적으로 불러올 수 있을까요?→ 방법은 콜백 함수안에 loadScript 를 호출하는 것, 이렇게 중첩 콜백을 만들면 바깥에 위치한 loadScript가 완료된 후, 안쪽 loadScript가 실행

 

!!! 콜백 안에 콜백을 넣는 것은 수행하려는 동작이 많은 경우엔 좋지 않은 방식

loadScript('/my/script.js', function(script) {

  alert(`${script.src}을 로딩했습니다. 이젠, 다음 스크립트를 로딩합시다.`);

  loadScript('/my/script2.js', function(script) {
    alert(`두 번째 스크립트를 성공적으로 로딩했습니다.`);
  });

});

 

에러 핸들링

loadScript에서 로딩 에러를 추적할 수 있게 기능을 개선

이러한 방식으로 에러를 다루는 패턴을 ⇒ 오류 우선 콜백(error-first callback) 이라고 한다

<예시>

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생했습니다.`));

  document.head.append(script);
}

 

<사용방식>

loadScript('/my/script.js', function(error, script) {
  if (error) {
    // 에러 처리
  } else {
    // 스크립트 로딩이 성공적으로 끝남
  }
});

 

오류 우선 콜백 의 관례

  1. callback의 첫 번째 인수는 에러를 위해 남겨둡니다. 에러가 발생하면 이 인수를 이용해 callback(err)이 호출됩니다.
  2. 두 번째 인수(필요하면 인수를 더 추가할 수 있음)는 에러가 발생하지 않았을 때를 위해 남겨둡니다. 원하는 동작이 성공한 경우엔 callback(null, result1, result2...)이 호출됩니다.

⇒ 오류 우선 콜백 스타일을 사용하면, 단일 콜백 함수에서 에러 케이스와 성공 케이스 모두를 처리할 수 있습니다.

 

멸망의 피라미드

  • 꼬리에 꼬리를 무는 비동기 동작이 많아지면 깊은 중첩이 있는 코드가 만들어진다
  • 이렇게 깊은 중첩 코드가 만들어내는 패턴은 소위 ‘콜백 지옥(callback hell)’ 혹은 '멸망의 피라미드(pyramid of doom)'라고 불립니다
loadScript('1.js', function(error, script) {

  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', function(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('3.js', function(error, script) {
          if (error) {
            handleError(error);
          } else {
            // 모든 스크립트가 로딩된 후, 실행 흐름이 이어집니다. (*)
          }
        });

      }
    })
  }
});

 

각 동작을 독립적인 함수로 만들어 위와 같은 문제를 완화하는 방법?

loadScript('1.js', step1);

function step1(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', step2);
  }
}

function step2(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('3.js', step3);
  }
}

function step3(error, script) {
  if (error) {
    handleError(error);
  } else {
    // 모든 스크립트가 로딩되면 다른 동작을 수행합니다. (*)
  }
};
  • 각 동작을 분리해 최상위 레벨의 함수로 만들었기 때문에 깊은 중첩이 없습니다.
  • 코드가 여기저기 흩어져 보이는 문제가 있다.
  • 게다가 step*이라고 명명한 함수들은 '멸망의 피라미드’를 피하려는 용도 만으로 만들었기 때문에 재사용이 불가능

→ 이를 해결할 수 있는 가장 좋은 방법 중 하나는 “프라미스” 를 사용하는 것이다.

 

참고자료

https://ko.javascript.info/async

원시값 과 참조값

  원시값 (Primitive value) 참조값 (Reference value)
데이터 타입 문자, 숫자, boolean, null, undefined, symbol 배열, 객체, 클래스, 함수
값의 변경 변경 불가능한 값 변경 가능한 값
저장 형태 변수에 지정된 값을 직접 저장 변수에 값이 저장된 메모리의 주소를 저장

 

여기서 변경 불가능 하다는 것은 원시값 자체를 변경하지 못 한다는 것 변수의 값은 변경가능

 

원시값을 할당 받은 변수의 값 변경 예시

let test = 1;
let changeValue = test;
test = 4
console.log(test) // 4
console.log(changeValue) // 1

  → 변수의 메모리에 저장된 값을 변경

 

참조값을 할당 받은 변수의 값 변경 예시

let arr = [1,2,3,4];
let result = arr
arr.push(5)

console.log(arr)    // [1,2,3,4,5]
console.log(result) // [1,2,3,4,5]

→ 배열이 저장된 메모리주소를  변수 arr 에 저장

→ result 에도 메모리 주소를 저장

→ arr.push(5) 는 arr 이 가르키는 메모리주소에 있는 값을 변경

→ result 와 arr 이 가르키는 메모리주소가 같으므로 같은 값을 출력

 

얕은 복사 (shallow copy) 와 깊은 복사 (deep copy) 

객체의 얕은 복사  = 복사본이 원본 객체와 같은 메모리 주소를 참조하는 복사

얕은 복사예시

// 배열
const a = [1, {name: "test"}]
const b = Array.from(a)

console.log(a === b) // false
console.log(a[0] === b[0]) // true
console.log(a[1].name === b[1].name) // true

// 값을 변경
a[1].name = "change"
a[0] = 4

/* 
a 의 첫 번째 값이 원시값이므로 b에는 반영이 안되었지만
a 의 두 번째 값은 참조값이여서 b에도 반영됨
*/
console.log(a) // [4, {name: "change"}]
console.log(b) // [1, {name: "change"}]

  얕은 복사는 객체와 같은 타입의 값은 메모리의 주소를 복사하므로 위와 같이 변경사항이 반영됨

 

깊은 복사 = 복사본이 원본과 같은 메모리주소를 참조(공유)하지 않는 복사

깊은 복사 예시

const aList = ["noodles", { list: ["eggs", "flour", "water"] }];
const aListDeepCopy = JSON.parse(JSON.stringify(aList));

aList[1].list = ["rice flour", "water"];

/*
aList 의 두 번째 요소와 aListDeepCopy 의 두 번째 요소가 
같은 메모리주소를 공유하지 않으므로 변경된 사항이 반영이 안됨
*/
console.log(aList)         // ["noodles", { list: ["rice flour", "water"] }]
console.log(aListDeepCopy) // ["noodles", { list: ["eggs", "flour", "water"] }]

 

 

참고 자료

https://developer.mozilla.org/ko/docs/Glossary/Shallow_copy

https://developer.mozilla.org/ko/docs/Glossary/Deep_copy

불변 객체

자바스크립트에서 객체의 상태(즉, 프로퍼티의 값)가 생성 후 변경될 수 없는 객체를 의미

객체의 불변성을 유지한다는 것

 

사용하는 경우

  • 객체에 변화를 가해도 원본이 그대로 남아있어야 하는 경우
  • 특히, 메모리(데이터베이스)에 저장 되어 있는 객체를 다루는 경우
  • 불변객체를 이용하여 상태변화를 추적해야하는 경우

사용하는 이유

  • 오류감소 : 객체의 상태가 변경되지 않기 때문에 예기치 않은 상태 변경으로 인한 버그감소
  • 성능최적화 : 복제나 비교를 위한 조작을 단순화 할 수 있고 성능 개선에도 도움

불변객체를 만드는 방법

Object.freeze() [ ref ]

const user= {
   name: "JJ",
   age : 38,
   address : {
     nation: "Korea",
     city: "JinJu"
   }
}

Object.freeze(user) // 상위레벨에 있는 것만 보호

user.name = "KK"
user.address.city = "Seoul"

console.log(user.name) // "JJ"
console.log(user.address.city) // "Seoul"


// deep freeze
const deepFreeze = obj => {
  Object.keys(obj).forEach(prop => {
    if (typeof obj[prop] === 'object') deepFreeze(obj[prop]);
  });
  return Object.freeze(obj);
};

 

Spread 연산자 [ ref ]

- 새로운 객체를 만들 때 사용

const obj1 = { type: 'data' }
const arr1 = [1, 2, 3]

const obj2 = { ...obj1, subtype: 'stuff' } // 원본 유지하면서 새로운 프로퍼티 추가
const arr2 = [ ...arr1, 'cheese' ] // 새로운 요소 추가


const data = { type: 'data', age: 55 }
const data2 = { ...obj1, age: data.age + 1 }// 원본 유지하면서 기존의 데이터 변경

 

 

꼭 변환을 해야한다면 깊은 복사를 하여  복사본을 변경을 하는 것을 추천한다.

// 스프레드 연산자

const originalArray = [ 1,2, 3]
const deepCopyArray = [ ... originalArray]

const user= {
   name: "JJ",
   age : 38,
   address : {
     nation: "Korea",
     city: "JinJu"
   }
}

// 1 단으로만 이루어진 객체에서는 가능
// spread
const copyUser = { ...user}
copyUser.name = "N"
copyUser.address.city = "Seoul"

// 원본 user 가 변경되었는지 확인
console.log(user.name) // "JJ" : 변경 안됨
console.log(user.address.city) // "Seoul" : "JinJu" 에서 "Seoul" 로 변경

// Object.assign 도 동일
const copyUserObjectAssign = Object.assign({}, user);


/* 1단 이상인 경우 */

// JSON 이용
const userCopyJSON = JSON.parse(JSON.stringify(user));


// 재귀함수 이용
function CopyObjectDeeply(target) {
  var result = {};

  if (typeof target === "object" && target !== null) {
    for (var prop in target) {
      result[prop] = copyObjectDeep(target[prop]); // 재귀적 호출
    }
  } else {
    result = target;
  }

  return result;
};

const userCopyFunction = CopyObjectDeeply(user)

 

 

참고자료

https://velog.io/@jujusnake/JavaScript-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4-immutable-objec#%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

 

[JavaScript] 불변 객체 immutable objec

참조형 데이터가 완전히 불변성을 띄도록 만들 수는 없을까?

velog.io

https://velog.io/@gusdh2/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8JavaScript-%EA%B0%9D%EC%B2%B4%EC%99%80-%EB%B6%88%EB%B3%80%EC%84%B1#%EB%B6%88%EB%B3%80%EC%84%B1%EC%9D%B4%EB%9E%80

 

자바스크립트(JavaScript) 객체와 불변성

불변성(Immutability)은 객체가 생성된 이후 그 상태를 변경할 수 없는 것을 의미합니다. 불변성은 함수형 프로그래밍의 핵심 원리이다.불변 객체를 사용하면 복제나 비교를 위한 조작을 단순화 할

velog.io

 

 

https://developer.mozilla.org/ko/docs/Glossary/Deep_copy

 

깊은 복사 - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN

객체의 깊은 복사는 복사본의 속성이 복사본이 만들어진 원본 객체와 같은 참조(메모리 내의 같은 값을 가리킴)를 공유하지 않는 복사입니다. 따라서 원본이나 복사본을 변경할 때, 다른 객체가

developer.mozilla.org

https://www.freecodecamp.org/news/immutable-javascript-improve-application-performance/

 

Immutable JavaScript – How to Improve the Performance of Your JS Applications

Javascript has become a very popular programming language thanks to its growing use in frontend and backend development. And as devs build JavaScript applications for different companies and organizations, the size and complexity of these applications can

www.freecodecamp.org

https://www.freecodecamp.org/news/javascript-immutability-frozen-objects-with-examples/

 

JavaScript Immutability – Frozen Objects in JS Explained with Examples

In JavaScript, we use an Object to store multiple values as a complex data structure. You create an object with a pair of curly braces {}. An object can have one or more properties. Each of the properties is a key-value pair separated by a colon(:). The ke

www.freecodecamp.org

https://dev.to/glebec/four-ways-to-immutability-in-javascript-3b3l

 

Four Ways to Immutability in JavaScript

An interactive look at four persistent data techniques

dev.to

 

 

Promise.all() 은 요청한 것이 모두 성공해야 값을 반환하므로 요청한 모든 값이 필요할 때 사용을 하면 좋다.

 

Promise.allSettled() 은 요청이 성공하던 실패를 하던 모든 값을 반환해준다.

 

코드예시

const urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/Violet-Bora-Lee',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => { // (*)
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });

 

결과

[
  {status: 'fulfilled', value: ...응답...},
  {status: 'fulfilled', value: ...응답...},
  {status: 'rejected', reason: ...에러 객체...}
]

 

스펙에 추가된 지 얼마 안 된 문법이라 지원이 안되면 구현해야한다고 한다.

//https://ko.javascript.info/promise-api#ref-1314
if(!Promise.allSettled) {
  Promise.allSettled = function(promises) {
    return Promise.all(promises.map(p => Promise.resolve(p).then(value => ({
      status: 'fulfilled',
      value
    }), reason => ({
      status: 'rejected',
      reason
    }))));
  };
}

 

 

사용하는 이유 및 상황

 

모든 값이 무조건 다 필요하지 않는 경우.

실패한 경우에 다르게 처리를 해줄 필요가 있는 경우.

    (예시) 한 해쉬태그가 있는지 조회를 했을 때 없는경우 -> 해당 해쉬태그를 등록

 

 

 

참고자료

https://ko.javascript.info/promise-api#ref-1314

 

프라미스 API

 

ko.javascript.info

 

+ Recent posts