WEB개발/JS

[JS] JS의 비동기 작업 ( = promise)

wooyeon06 2021. 6. 24. 13:34

 비동기란 특정 코드의 연산이 끝날 때까지 코드의 실행이 끝나지 않은 상태에서, 순차적으로 다음 코드를 먼저 실행하는 JS의 특성이다. 요청에 대한 결과를 기다리지 않고 다음 코드를 실행한다는 것이다. 

 

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.

 

var promiseCount = 0;

function testPromise() {
    var thisPromiseCount = ++promiseCount;

    var log = document.getElementById('log');
    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') 시작 (<small>동기적 코드 시작</small>)<br/>');

    // 새 프로미스 생성 - 프로미스의 생성 순서를 전달하겠다는 약속을 함 (3초 기다린 후)
    var p1 = new Promise(
        // 실행 함수는 프로미스를 이행(resolve)하거나
        // 거부(reject)할 수 있음
        function(resolve, reject) {
            log.insertAdjacentHTML('beforeend', thisPromiseCount +
                ') 프로미스 시작 (<small>비동기적 코드 시작</small>)<br/>');
            // setTimeout은 비동기적 코드를 만드는 예제에 불과
            window.setTimeout(
                function() {
                    // 프로미스 이행 !
                    resolve(thisPromiseCount);
                }, Math.random() * 2000 + 1000);
        }
    );

    // 프로미스를 이행했을 때 할 일은 then() 호출로 정의하고,
    // 거부됐을 때 할 일은 catch() 호출로 정의
    p1.then(
        // 이행 값 기록
        function(val) {
            log.insertAdjacentHTML('beforeend', val +
                ') 프로미스 이행 (<small>비동기적 코드 종료</small>)<br/>');
        })
    .catch(
        // 거부 이유 기록
        function(reason) {
            console.log('여기서 거부된 프로미스(' + reason + ')를 처리하세요.');
        });

    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') 프로미스 생성 (<small>동기적 코드 종료</small>)<br/>');
}

>

1) 시작 (동기적 코드 시작)
1) 프로미스 시작 (비동기적 코드 시작)
1) 프로미스 생성 (동기적 코드 종료)
1) 프로미스 이행 (비동기적 코드 종료)

 

 

 이전까지 비동기화 작업은 주로 callback을 사용했다. 그러나 callback은 몇가지 문제가 있어 이를 보완하기 위해  promise가 등장한다.

 

문제1. 비동기 처리 결과를 외부에 반환하지 못함

  const get = url => {
	const xhr = new XMLHttpRequest() // XMLHttpRequest 객체생성
  xhr.open('GET', url) // HTTP요청 초기화
  xhr.send() // HTTP요청 전송
  
  xhr.onload = () => { // onload 이벤트 핸들러 프로퍼티에 바인딩하고 종료
    if (xhr.status === 200) {
      return JSON.parse(xhr.response)
    }
    console.error(`${xhr.status} ${xhr.statusText}`)
  }
}

const response = get('https://jsonplaceholder.typicode.com/posts/1')
console.log(response) // undefined

response를 반환하지만 undefinded인 이유는 응답을 받기 전에 이미 console.log가 수행되기 때문이다.

 

문제2. 비동기 처리 결과를 상위 스코프의 변수에 할당할 수 없음

let todos;

const get = url => {
  const xhr = new XMLHttpRequest()
  xhr.open('GET', url)
  xhr.send()
  
  xhr.onload = () => {
    if (xhr.status === 200) {
      todos = JSON.parse(xhr.response)
    } else {
      console.error(`${xhr.status} ${xhr.statusText}`)
    }
  }
}

get('https://jsonplaceholder.typicode.com/posts/1')

수행 후 todos를 console.log로 확인해도 undefined이다 todos의 스코프가 다르기 때문이다.

 

 위 두가지 문제를 해결하기 위해 promise를 이용해봤지만 맞는지 모르겠다..... 

let result = "";


var promiseFactory = function() {
	return new Promise((resolve, reject) => {

		const get = url => {
			const xhr = new XMLHttpRequest()
			xhr.open('GET', url)
			xhr.send()
			
			xhr.onload = () => {
			  if (xhr.status === 200) {
			    todos = JSON.parse(xhr.response);
			    resolve(todos);
			  } else {
			    console.error(`${xhr.status} ${xhr.statusText}`);
			  }
			}
		}
		get('https://jsonplaceholder.typicode.com/posts/1');
	})
};


promiseFactory().then((todos)=> {
	result = todos;
	console.log("todos > " + JSON.stringify(todos));
}).then(promiseFactory).then((todos) => {
	console.log("todos > " + JSON.stringify(todos));
	
});

 

위와 같은 방식으로 작성하면.. 콜백의 콜백의 겹겹이 중첩된 블락구조를 피할 수 있고 전역인 result변수에 값도 전달가능하다. 

 

function getData() {
  return new Promise((resolve, reject) => {
    // 비동기 작업 수행
    setTimeout(() => {
      const data = 'Hello, World!';
      resolve(data);
    }, 1000);
  });
}

getData()
  .then(result => {
    console.log('첫 번째 데이터:', result);
    // 다음 비동기 작업을 위한 Promise 반환
    return getData();
  })
  .then(result => {
    console.log('두 번째 데이터:', result);
    // 다음 비동기 작업을 위한 Promise 반환
    return getData();
  })
  .then(result => {
    console.log('세 번째 데이터:', result);
  })
  .catch(error => {
    console.error('오류 발생:', error);
  });

 

참고

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
 

Promise - JavaScript | MDN

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

developer.mozilla.org

https://velog.io/@eunjin/JavaScript-Promise-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-%EA%B8%B0%EB%B3%B8-%EB%A9%94%EC%84%9C%EB%93%9C-3%EA%B0%80%EC%A7%80-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0