728x90
반응형
비동기 처리
- 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트 특성
콜백
- 나중에 호출할 함수를 의미
비동기 동작
- 스크립트 함수 비동기 동작 처리 예시
- 콜백함수 추가 전
function loadScript(src) { // <script> 태그를 만들고 페이지에 태그를 추가합니다. // 태그가 페이지에 추가되면 src에 있는 스크립트를 로딩하고 실행합니다. let script = document.createElement('script'); script.src = src; document.head.append(script); } // loadScript(src)는 <script src="…">를 동적으로 만들고 이를 문서에 추가 // 해당 경로에 위치한 스크립트를 불러오고 '비동기적으로' 실행 // 실행은 함수가 끝난 후 loadScript('/my/script.js'); // loadScript 아래의 코드는 // 스크립트 로딩이 끝날 때까지 기다리지 않습니다. newFunction(); // 함수가 존재하지 않는다는 에러 발생
- 콜백함수 추가 후 - ‘콜백 기반(callback-based)’ 비동기 처리
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(); // 이제 함수 호출이 제대로 동작합니다. ... });
- setTimeout : 스케줄링에 사용되는 대표적 함수
콜백 속 콜백
loadScript('/my/script.js', function(script) {
alert(`${script.src}을 로딩했습니다. 이젠, 다음 스크립트를 로딩합시다.`);
loadScript('/my/script2.js', function(script) {
alert(`두 번째 스크립트를 성공적으로 로딩했습니다.`);
loadScript('/my/script3.js', function(script) {
// 세 스크립트 로딩이 끝난 후 실행됨
});
});
});
에러 핸들링
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);
}
멸망의 피라미드
- 꼬리에 꼬리를 무는 비동기 동작이 많아졌을 때 다중 코드 중첩이 발생
프라미스
- 자바스크립트 비동기 처리에 사용되는 객체
프로미스의 3가지 상태
- Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
- new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject가 됨.
new Promise(function(resolve, reject) { // ... });
- Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
- 이행 상태가 되면 아래와 같이 then()을 이용하여 처리 결과 값을 받을 수 있다.
function getData() { return new Promise(function(resolve, reject) { var data = 100; resolve(data); }); } // resolve()의 결과 값 data를 resolvedData로 받음 getData().then(function(resolvedData) { console.log(resolvedData); // 100 });
- Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
- 실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()로 받을 수 있다.
function getData() { return new Promise(function(resolve, reject) { reject(new Error("Request is failed")); }); } // reject()의 결과 값 Error를 err에 받음 getData().then().catch(function(err) { console.log(err); // Error: Request is failed });
- 처리 흐름
가수와 팬, 구독 리스트
- '제작 코드(producing code)'는 원격에서 스크립트를 불러오는 것 같은 시간이 걸리는 일을 합니다. 위 비유에선 '가수’=executor가 제작 코드에 해당.
- '소비 코드(consuming code)'는 '제작 코드’의 결과를 기다렸다가 이를 소비. 이때 소비 주체(함수)는 여럿이 될 수 있습니다. 위 비유에서 소비 코드는 '팬’입니다.
- 프라미스(promise) 는 '제작 코드’와 '소비 코드’를 연결해 주는 특별한 자바스크립트 객체
- 위 비유에서 프라미스는 '구독 리스트’입니다. '프라미스’는 시간이 얼마나 걸리든 상관없이 약속한 결과를 만들어 내는 '제작 코드’가 준비되었을 때, 모든 소비 코드가 결과를 사용할 수 있도록 해줍니다.
- 프라미스 기본 문법
let promise = new Promise(function(resolve, reject) {
// executor (제작 코드, '가수')
});
executor(실행자, 실행 함수)
new Promise에 전달되는 함수
- 자바스크립트 자체 콜백 executor 인수
- resolve(value) — 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
- reject(error) — 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
- new Promise 생성자가 반환하는 promise 객체의 내부 Property
- state — 처음엔 "pending"(보류)이었다 resolve가 호출되면 "fulfilled", reject가 호출되면 "rejected".
- result — 처음엔 undefined이었다 resolve(value)가 호출되면 value로, reject(error)가 호출되면 error.
fulfilled promise(약속이 이행된 프라미스)
let promise = new Promise(function(resolve, reject) {
// 프라미스가 만들어지면 executor 함수는 자동으로 실행됩니다.
// 1초 뒤에 일이 성공적으로 끝났다는 신호가 전달되면서 result는 '완료'가 됩니다.
setTimeout(() => resolve("완료"), 1000);
});
- executor는 new Promise에 의해 자동으로 그리고 즉각적으로 호출.
- executor는 인자로 resolve와 reject 함수를 받습니다. 이 함수들은 자바스크립트 엔진이 미리 정의한 함수이므로 개발자가 따로 만들 필요가 없습니다. 다만, resolve나 reject 중 하나는 반드시 호출 필요.
- executor '처리’가 시작 된 지 1초 후, resolve("완료")이 호출되고 결과가 만들어집니다. 이때 promise 객체의 상태는 다음과 같이 변합니다.
settled promise(처리된 프라미스)
let promise = new Promise(function(resolve, reject) {
// 1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
이행(resolved) 혹은 거부(rejected) 상태의 프라미스
then, catch, finally
소비함수 : .then, .catch, .finally
then
promise.then(
function(result) { /* 결과(result)를 다룹니다 */ },
function(error) { /* 에러(error)를 다룹니다 */ }
);
- 첫 번째 인수 프라미스가 이행되었을 때 실행되는 함수 ex) 실행 결과
- 두 번째 인수 프라미스가 거부되었을 때 실행되는 함수 ex) 에러
- 작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달
catch
- .catch(f) === .then(null,f)
- 에러가 발생하는 경우만 다루고 싶을 때 : .then(null, errorHandlingFunction)같이 null을 첫 번째 인수로 전달
- .catch(errorHandlingFunction)를 써도 되는데, .catch는 .then에 null을 전달하는 것과 동일하게 작동
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력
finally
- .finally(f) == .then(f, f) .finally(f)가 문법측면에서 더 편리
- then(f, f)와의 차이점
- finally 핸들러엔 인수 X. finally에선 프라미스가 이행되었는지, 거부되었는지 모름. finally에선 절차를 마무리하는 ‘보편적’ 동작을 수행하기 때문에 성공·실패 여부를 몰라도 됨.
- finally 핸들러는 자동으로 다음 핸들러에 결과와 에러를 전달
new Promise((resolve, reject) => {
setTimeout(() => resolve("결과"), 2000)
})
.finally(() => alert("프라미스가 준비되었습니다."))
.then(result => alert(result)); // <-- .then에서 result를 다룰 수 있음
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
})
.finally(() => alert("프라미스가 준비되었습니다."))
.catch(err => alert(err)); // <-- .catch에서 에러 객체를 다룰 수 있음
반응형
'언어 > Script Question' 카테고리의 다른 글
$와 _는 무엇인가 (0) | 2024.04.07 |
---|---|
동기/비동기 & 블로킹/논블록킹 (0) | 2024.04.07 |
Web Storage는 무엇인가 (0) | 2024.03.31 |
IoC / DI가 무엇인가 (0) | 2024.03.31 |
What is This? (0) | 2023.10.30 |