JavaScript 약속: 예제가 포함된 자습서

게시 됨: 2022-03-11

Promise는 JavaScript 개발 서클에서 뜨거운 주제이며 확실히 숙지해야 합니다. 그들은 당신의 머리를 감싸기가 쉽지 않습니다. 이를 이해하려면 몇 가지 자습서, 예제 및 상당한 양의 연습이 필요할 수 있습니다.

이 튜토리얼의 목표는 JavaScript Promise를 이해하고 더 많이 사용하도록 유도하는 것입니다. Promise가 무엇인지, 어떤 문제를 해결하는지, 어떻게 작동하는지 설명하겠습니다. 이 기사에서 설명하는 각 단계에는 작업을 돕고 추가 탐색을 위한 기반으로 사용할 jsbin 코드 예제가 함께 제공됩니다.

JavaScript 약속은 이 포괄적인 자습서에서 설명합니다.

JavaScript 약속이란 무엇입니까?

Promise는 결국 값을 생성하는 방법입니다. getter 함수의 비동기 대응물로 간주할 수 있습니다. 그 본질은 다음과 같이 설명할 수 있습니다.

 promise.then(function(value) { // Do something with the 'value' });

Promise는 비동기식 콜백 사용을 대체할 수 있으며 이에 비해 몇 가지 이점이 있습니다. 점점 더 많은 라이브러리와 프레임워크가 비동기성을 처리하는 기본 방법으로 이를 수용함에 따라 기반을 확보하기 시작했습니다. Ember.js는 이러한 프레임워크의 좋은 예입니다.

Promises/A+ 사양을 구현하는 여러 라이브러리가 있습니다. 우리는 기본적인 어휘를 배우고, 실용적인 방법으로 그 뒤에 숨겨진 개념을 소개하기 위해 몇 가지 JavaScript 약속 예제를 통해 작업할 것입니다. 코드 예제에서 가장 널리 사용되는 구현 라이브러리 중 하나인 rsvp.js를 사용하겠습니다.

준비하세요, 우리는 많은 주사위를 굴릴 것입니다!

rsvp.js 라이브러리 가져오기

Promise와 rsvp.js는 서버 측과 클라이언트 측 모두에서 사용할 수 있습니다. nodejs 용으로 설치하려면 프로젝트 폴더로 이동하여 다음을 입력합니다.

 npm install --save rsvp

프론트 엔드에서 작업하고 바우어를 사용하는 경우

 bower install -S rsvp

떨어져있는.

게임에 바로 들어가고 싶다면 간단한 스크립트 태그를 통해 포함할 수 있습니다( jsbin 을 사용하여 "라이브러리 추가" 드롭다운을 통해 추가할 수 있음).

 <script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>

Promise에는 어떤 속성이 있습니까?

Promise 가지 상태 중 하나일 수 있습니다 . 생성될 때 약속은 보류 상태입니다. 여기에서 처리됨 또는 거부됨 상태로 이동할 수 있습니다. 우리는 이 전환 을 약속의 해결 이라고 부릅니다. Promise의 해결된 상태는 최종 상태이므로 일단 이행되거나 거부되면 그대로 유지됩니다.

rsvp.js에서 Promise를 생성하는 방법은 계시 생성자를 통하는 것입니다. 이 유형의 생성자는 단일 함수 매개변수를 사용하고 두 개의 인수인 fulfillreject 를 사용하여 즉시 호출하여 약속을 fulfilled 또는 rejected 상태로 전환할 수 있습니다.

 var promise = new RSVP.Promise(function(fulfill, reject) { (...) });

이 JavaScript Promise 패턴은 단일 함수 인수가 생성자 기능에 대한 기능을 나타내지만 Promise의 소비자가 상태를 조작할 수 없도록 하기 때문에 표시 생성자라고 합니다.

Promise의 소비자는 then 메서드를 통해 처리기를 추가하여 상태 변경에 반응할 수 있습니다. 처리 및 거부 처리기 기능이 필요하며 둘 다 누락될 수 있습니다.

 promise.then(onFulfilled, onRejected);

Promise의 해결 프로세스 결과에 따라 onFulfilled 또는 onRejected 핸들러가 비동기적 으로 호출됩니다.

실행되는 순서를 보여주는 예를 살펴보겠습니다.

 function dieToss() { return Math.floor(Math.random() * 6) + 1; } console.log('1'); var promise = new RSVP.Promise(function(fulfill, reject) { var n = dieToss(); if (n === 6) { fulfill(n); } else { reject(n); } console.log('2'); }); promise.then(function(toss) { console.log('Yay, threw a ' + toss + '.'); }, function(toss) { console.log('Oh, noes, threw a ' + toss + '.'); }); console.log('3');

이 스니펫은 다음과 유사한 출력을 인쇄합니다.

 1 2 3 Oh, noes, threw a 4.

또는 운이 좋으면 다음이 표시됩니다.

 1 2 3 Yay, threw a 6.

이 약속 튜토리얼은 두 가지를 보여줍니다.

첫째, promise에 첨부한 핸들러는 다른 모든 코드가 비동기적으로 실행된 후에 실제로 호출되었습니다.

둘째, 이행 핸들러는 약속이 이행되었을 때만 호출되었으며, 이 값으로 해결되었습니다(이 경우에는 주사위 던지기 결과). 거부 처리기의 경우에도 마찬가지입니다.

약속을 엮고 흘러내리는

사양에서는 then 함수(핸들러)도 약속을 반환해야 하므로 약속을 함께 연결할 수 있으므로 거의 동기적으로 보이는 코드가 생성됩니다.

 signupPayingUser .then(displayHoorayMessage) .then(queueWelcomeEmail) .then(queueHandwrittenPostcard) .then(redirectToThankYouPage)

여기에서 signupPayingUser 는 promise를 반환하고 promise 체인의 각 함수는 완료되면 이전 핸들러의 반환 값으로 호출됩니다. 모든 실제적인 목적을 위해 기본 실행 스레드를 차단하지 않고 호출을 직렬화합니다.

체인에 있는 이전 항목의 반환 값으로 각 약속이 어떻게 해결되는지 보기 위해 주사위 던지기로 돌아갑니다. 주사위를 최대 3번 던지거나 처음 6번이 jsbin이 나올 때까지 던지고 싶습니다.

 function dieToss() { return Math.floor(Math.random() * 6) + 1; } function tossASix() { return new RSVP.Promise(function(fulfill, reject) { var n = Math.floor(Math.random() * 6) + 1; if (n === 6) { fulfill(n); } else { reject(n); } }); } function logAndTossAgain(toss) { console.log("Tossed a " + toss + ", need to try again."); return tossASix(); } function logSuccess(toss) { console.log("Yay, managed to toss a " + toss + "."); } function logFailure(toss) { console.log("Tossed a " + toss + ". Too bad, couldn't roll a six"); } tossASix() .then(null, logAndTossAgain) //Roll first time .then(null, logAndTossAgain) //Roll second time .then(logSuccess, logFailure); //Roll third and last time

이 Promise 예제 코드를 실행하면 콘솔에 다음과 같은 내용이 표시됩니다.

 Tossed a 2, need to try again. Tossed a 1, need to try again. Tossed a 4. Too bad, couldn't roll a six.

tossASix 가 반환한 promise는 toss가 6이 아닐 때 거부되므로 실제 toss와 함께 거부 핸들러가 호출됩니다. logAndTossAgain 은 그 결과를 콘솔에 출력하고 또 다른 주사위 던지기를 나타내는 약속을 반환합니다. 그 던지기는 차례로 다음 logAndTossAgain 에 의해 거부되고 로그아웃됩니다.

그러나 때때로 운이 좋아서* 6을 굴릴 수 있습니다.

 Tossed a 4, need to try again. Yay, managed to toss a 6.

* 그렇게 운이 좋을 필요는 없습니다. 주사위 3개를 굴릴 경우 6개 이상을 굴릴 확률은 ~42%입니다.

그 예는 또한 우리에게 더 많은 것을 가르쳐줍니다. 처음으로 6을 성공적으로 던진 후 더 이상 토스가 없었는지 확인하십시오. 체인의 모든 처리 핸들러( then 에 대한 호출의 첫 번째 인수)는 마지막 logSuccess 를 제외하고 null 입니다. 사양은 핸들러(이행 또는 거부)가 함수가 아닌 경우 반환된 약속이 동일한 값으로 해결(이행 또는 거부)되어야 한다고 요구합니다. 위의 promise 예제에서 fulfillment 핸들러 null 은 함수가 아니며 promise의 값은 6으로 이행되었습니다. 따라서 then 호출(체인의 다음 호출)에 의해 반환된 promise도 이행될 것입니다. 6을 값으로 사용합니다.

이것은 실제 fulfillment 핸들러(함수인 핸들러)가 나타날 때까지 반복되므로 처리가 처리될 때까지 fulfillment가 조금씩 떨어 집니다. 우리의 경우 이것은 콘솔에 유쾌하게 로그아웃되는 체인의 끝에서 발생합니다.

오류 처리

Promises/A+ 사양은 약속이 거부되거나 거부 핸들러에서 오류가 발생하는 경우 소스에서 "다운스트림"에 있는 거부 핸들러에 의해 처리되어야 한다고 요구합니다.

아래의 트리클 다운 기술을 활용하면 오류를 처리하는 깔끔한 방법을 얻을 수 있습니다.

 signupPayingUser .then(displayHoorayMessage) .then(queueWelcomeEmail) .then(queueHandwrittenPostcard) .then(redirectToThankYouPage) .then(null, displayAndSendErrorReport)

거부 핸들러는 체인의 맨 끝에만 추가되기 때문에 체인의 처리 핸들러가 거부되거나 오류가 발생하면 displayAndSendErrorReport 에 부딪힐 때까지 계속 흘러내립니다.

우리의 사랑하는 주사위로 돌아가서 그것이 실제로 작동하는지 봅시다. 주사위를 비동기적으로 던지고 결과를 출력하고 싶다고 가정해 봅시다:

 var tossTable = { 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six' }; function toss() { return new RSVP.Promise(function(fulfill, reject) { var n = Math.floor(Math.random() * 6) + 1; fulfill(n); }); } function logAndTossAgain(toss) { var tossWord = tossTable[toss]; console.log("Tossed a " + tossWord.toUppercase() + "."); } toss() .then(logAndTossAgain) .then(logAndTossAgain) .then(logAndTossAgain);

이것을 실행하면 아무 일도 일어나지 않습니다. 콘솔에 아무 것도 인쇄되지 않으며 겉보기에는 오류가 발생하지 않습니다.

실제로 오류가 발생하지만 체인에 거부 처리기가 없기 때문에 오류가 표시되지 않습니다. 핸들러의 코드는 새로운 스택을 사용하여 비동기적으로 실행되기 때문에 콘솔에 로그아웃되지도 않습니다. 이 문제를 해결해 보겠습니다.

 function logAndTossAgain(toss) { var tossWord = tossTable[toss]; console.log("Tossed a " + tossWord.toUpperCase() + "."); } function logErrorMessage(error) { console.log("Oops: " + error.message); } toss() .then(logAndTossAgain) .then(logAndTossAgain) .then(logAndTossAgain) .then(null, logErrorMessage);

위의 코드를 실행하면 이제 오류가 표시됩니다.

 "Tossed a TWO." "Oops: Cannot read property 'toUpperCase' of undefined"

logAndTossAgain 에서 무언가를 반환하는 것을 잊었고 두 번째 약속은 undefined 로 이행되었습니다. 다음 fulfillment 핸들러는 이에 대해 toUpperCase 를 호출하려고 시도합니다. 기억해야 할 또 다른 중요한 사항입니다. 항상 처리기에서 무언가를 반환하거나 후속 처리기에서 아무 것도 전달되지 않도록 준비하십시오.

더 높은 빌딩

이제 이 자습서의 예제 코드에서 JavaScript 약속의 기본 사항을 보았습니다. 그것들을 사용하는 것의 큰 이점은 우리가 원하는 동작으로 "복합" 약속을 생성하기 위해 간단한 방법으로 구성할 수 있다는 것입니다. rsvp.js 라이브러리는 몇 가지를 제공하며, 언제든지 기본 요소와 이러한 상위 수준 라이브러리를 사용하여 자신만의 것을 만들 수 있습니다.

가장 복잡한 마지막 예를 들어, AD&D 롤플레잉의 세계로 이동하여 캐릭터 점수를 얻기 위해 주사위를 던집니다. 이러한 점수는 캐릭터의 각 기술에 대해 3개의 주사위를 굴려서 얻습니다.

먼저 여기에 코드를 붙여넣고 새로운 기능을 설명하겠습니다.

 function toss() { var n = Math.floor(Math.random() * 6) + 1; return new RSVP.resolve(n); // [1] } function threeDice() { var tosses = []; function add(x, y) { return x + y; } for (var i=0; i<3; i++) { tosses.push(toss()); } return RSVP.all(tosses).then(function(results) { // [2] return results.reduce(add); // [3] }); } function logResults(result) { console.log("Rolled " + result + " with three dice."); } function logErrorMessage(error) { console.log("Oops: " + error.message); } threeDice() .then(logResults) .then(null, logErrorMessage);

우리는 마지막 코드 예제의 toss 에 익숙합니다. 그것은 단순히 주사위를 던진 결과로 항상 성취되는 약속을 생성합니다. 나는 덜 의식적으로 그러한 약속을 생성하는 편리한 방법인 RSVP.resolve 를 사용했습니다(위 코드의 [1] 참조).

threeDice 에서 각각 주사위 던지기를 나타내는 3개의 약속을 만들고 마침내 RSVP.all 과 결합했습니다. RSVP.all 은 약속 배열을 취하고 순서를 유지하면서 각 구성 약속에 대해 하나씩 해결된 값의 배열로 해결됩니다. 즉, 결과에 던지기의 results 가 있고(위 코드의 [2] 참조) 합계로 이행된 약속을 반환합니다(위 코드의 [3] 참조).

결과 약속을 해결하면 총 수를 기록합니다.

 "Rolled 11 with three dice"

실제 문제를 해결하기 위해 약속 사용

JavaScript 약속은 비동기식 주사위 던지기 보다 훨씬 더 복잡한 응용 프로그램의 문제를 해결하는 데 사용됩니다.

세 개의 주사위를 굴리는 것을 별도의 엔드포인트에 세 개의 ajax 요청을 보내는 것으로 대체하고 모든 요청이 성공적으로 반환되었을 때(또는 그 중 하나가 실패한 경우) 진행하면 이미 promise 및 RSVP.all 의 유용한 응용 프로그램이 있는 것입니다.

프라미스는 올바르게 사용되면 추론하기 쉽고 따라서 콜백보다 디버그하기 쉬운 읽기 쉬운 코드를 생성합니다. 예를 들어 오류 처리와 관련된 규칙은 이미 사양의 일부이기 때문에 설정할 필요가 없습니다.

우리는 이 JavaScript 튜토리얼에서 promise가 할 수 있는 일의 표면을 간신히 긁었습니다. Promise 라이브러리는 원하는 대로 사용할 수 있는 수십 가지 메서드와 저수준 생성자를 제공합니다. 이것들을 마스터하면 하늘이 당신이 할 수 있는 것의 한계입니다.

저자 소개

Balint Erdi는 오래전에 훌륭한 롤플레잉 및 AD&D 팬이었고, 지금은 훌륭한 약속이자 Ember.js 팬입니다. 변함없는 것은 로큰롤에 대한 그의 열정입니다. 이것이 그가 책에서 응용 프로그램의 주제로 로큰롤을 사용하는 Ember.js에 책을 쓰기로 결정한 이유입니다. 언제 출시되는지 알아보려면 여기에서 등록하세요.