>  기사  >  웹 프론트엔드  >  비동기 JavaScript 프로그래밍_node.js에서 Promise를 사용하는 방법은 무엇입니까?1.1.5

비동기 JavaScript 프로그래밍_node.js에서 Promise를 사용하는 방법은 무엇입니까?1.1.5

PHP中文网
PHP中文网원래의
2016-05-16 15:48:361156검색


비동기식?

비동기(Asynchronous)라는 말을 여러 곳에서 본 적이 있지만, 이 개념을 완전히 이해하지 못할 때에는 "이미 명확하다"고 생각하는 경우가 많습니다(*  ̄? ̄ ).

비슷한 상황이시라면 이 단어로 검색하시면 대략적인 설명을 얻으실 수 있습니다. 여기서는 JavaScript의 비동기성에 대해 조금 더 설명하겠습니다.

이 코드를 보세요:

var start = new Date();
setTimeout(function(){
  var end = new Date();
  console.log("Time elapsed: ", end - start, "ms");
}, 500);
while (new Date - start < 1000) {};

이 코드를 실행하면 Time elapsed: 1013ms와 같은 결과를 얻게 됩니다. setTimeout()에 의해 앞으로 500ms 동안 실행되도록 설정된 함수는 실제로 실행되기까지 1000ms 이상을 기다린다.

어떻게 설명해야 할까요? setTimeout()이 호출되면 지연된 이벤트가 대기열에 추가됩니다. 그런 다음 더 이상 코드가 없을 때까지 이 이후의 코드를 계속 실행하고, 그 이후의 코드를 계속 실행합니다. 코드가 없으면 JavaScript 스레드는 유휴 상태가 됩니다. 이때 JavaScript 실행 엔진은 대기열을 살펴보고 대기열에서 "트리거되어야 하는" 이벤트를 찾은 다음 이 이벤트의 핸들러(함수)를 호출합니다. 프로세서는 실행을 완료한 후 큐로 돌아가서 다음 이벤트를 살펴봅니다.

단일 스레드 JavaScript는 대기열을 통해 이벤트 루프 형태로 작동합니다. 따라서 이전 코드에서는 코드가 실행되는 동안 최대 1000ms 동안 실행 엔진을 드래그하는 데 while을 사용했으며 모든 코드가 완료되어 큐에 반환될 때까지 이벤트가 트리거되지 않습니다. 이것이 JavaScript의 비동기 메커니즘입니다.
JavaScript의 비동기 문제

JavaScript의 비동기 작업이 항상 간단하지는 않을 수 있습니다.

Ajax는 아마도 우리가 가장 많이 사용하는 비동기 작업일 것입니다. jQuery를 예로 들면, Ajax 요청을 시작하는 코드는 일반적으로 다음과 같습니다.

// Ajax请求示意代码
$.ajax({
  url: url,
  data: dataObject,
  success: function(){},
  error: function(){}
});

이런 방식으로 작성하는 데 문제가 있나요? 간단히 말해서, 휴대성이 충분하지 않습니다. 요청이 시작될 때 성공 및 오류 콜백을 작성해야 하는 이유는 무엇입니까? 내 콜백이 매우 많은 일을 수행해야 한다면 여기로 돌아가서 한 가지 생각이 날 때 코드를 추가해야 합니까?

또 다른 예로 Ajax 액세스를 위한 URL 주소가 4개 있습니다. 먼저 Ajax를 통해 첫 번째 액세스가 완료된 후 반환된 주소를 사용합니다. 두 번째 매개변수로 얻은 데이터를 다시 액세스하고, 두 번째 액세스가 완료된 후 세 번째 매개변수에 액세스합니다... 이제부터 4번의 액세스가 모두 완료됩니다. 이렇게 작성하면 이렇게 되는 것 같습니다.

$.ajax({
  url: url1,
  success: function(data){
    $.ajax({
      url: url2,
      data: data,
      success: function(data){
        $.ajax({
          //...
        });
      }  
    });
  }
})

Pyramid of Doom이라는 코드가 이렇게 생겼다고 생각하실 겁니다. 끔찍한. 직접 연결된 콜백을 작성하는 데 익숙하다면 한 이벤트에서 다음 이벤트로 전달되는 비동기 이벤트에 대해 혼란스러울 수 있습니다. 이러한 콜백 함수에 별도의 이름을 지정하고 별도로 저장하면 형식의 중첩이 줄어들고 코드가 더 명확해질 수 있지만 여전히 문제가 해결되지는 않습니다.

또 다른 일반적인 어려움은 두 개의 Ajax 요청을 동시에 보낸 후 두 요청이 모두 성공적으로 반환된 후 다음 작업을 수행하기 위해 이전 방법을 따른다면 생각해 보세요. 호출 위치에 콜백을 첨부하는 것이 조금 어려운 것 같나요?

Promise는 이러한 비동기 작업을 처리하는 데 적합하며 더 우아한 코드를 작성할 수 있게 해줍니다.
무대에 오르는 약속

약속이란? 계속해서 이전 jQuery Ajax 요청 신호 코드를 예로 들어 보겠습니다. 해당 코드는 실제로 다음과 같이 작성할 수 있습니다.

var promise = $.ajax({
  url: url,
  data: dataObject
});
promise.done(function(){});
promise.fail(function(){});

이것은 이전 코드와 유사합니다. Ajax 요청 신호는 동일합니다. 보시다시피 Promise를 추가하면 코드 형식이 변경됩니다. Ajax 요청은 변수 할당과 마찬가지로 "저장"됩니다. 이것이 캡슐화이며, 캡슐화는 실제로 비동기 이벤트를 더 쉽게 만듭니다.
캡슐화가 유용합니다

Promise 객체는 비동기 이벤트에 대한 캡슐화된 참조와 같습니다. 이 비동기 이벤트가 완료된 후 뭔가를 하고 싶으십니까? 콜백만 첨부하면 됩니다. 아무리 많이 첨부해도 상관없습니다!

jQuery의 Ajax 메소드는 Promise 객체를 반환합니다(이는 jQuery 1.5에 추가된 핵심 기능입니다). 비동기 이벤트가 성공적으로 완료된 후 실행하고 싶은 두 개의 함수 do1() 및 do2()가 있는 경우 다음만 수행하면 됩니다.

promise.done(do1);
// Other code here.
promise.done(do2);

이 방법은 훨씬 더 무료입니다. 비동기 이벤트가 시작되는 위치에 관계없이 코드를 작성하는 동안 언제든지 이 Promise 객체를 저장하고 콜백을 원하는 만큼 연결하면 됩니다. 이것이 Promise의 장점입니다.
공식 소개

Promise는 비동기 작업에 매우 유용하여 Promises/A라는 CommonJS 사양으로 개발되었습니다. Promise는 작업이 완료된 후 반환 값을 나타냅니다.

  1. Promise 작업이 성공했음을 나타내는 양수(이행 또는 해결)의 세 가지 상태가 있습니다.

  2. 부정(거부 또는 실패)은 Promise 작업이 실패했음을 나타냅니다.

  3. 대기 중(보류 중), 아직 긍정적이거나 부정적인 결과가 나오지 않고 진행 중입니다.

또한 Promise 작업의 성공 또는 실패를 나타내는 데 사용되는 명목 상태가 있는데, 이는 확정이라고 하는 긍정적인 상태와 부정적인 상태의 모음입니다. Promise에는 다음과 같은 중요한 기능도 있습니다:

  •     一个Promise只能从等待状态转变为肯定或否定状态一次,一旦转变为肯定或否定状态,就再也不会改变状态。

  •     如果在一个Promise结束(成功或失败,同前面的说明)后,添加针对成功或失败的回调,则回调函数会立即执行。

想想Ajax操作,发起一个请求后,等待着,然后成功收到返回或出现错误(失败)。这是否和Promise相当一致?

进一步解释Promise的特性还有一个很好的例子:jQuery的$(document).ready(onReady)。其中onReady回调函数会在DOM就绪后执行,但有趣的是,如果在执行到这句代码之前,DOM就已经就绪了,那么onReady会立即执行,没有任何延迟(也就是说,是同步的)。
Promise示例
生成Promise

Promises/A里列出了一系列实现了Promise的JavaScript库,jQuery也在其中。下面是用jQuery生成Promise的代码:

var deferred = $.Deferred();
deferred.done(function(message){console.log("Done: " + message)});
deferred.resolve("morin"); // Done: morin

jQuery自己特意定义了名为Deferred的类,它实际上就是Promise。$.Deferred()方法会返回一个新生成的Promise实例。一方面,使用deferred.done()、deferred.fail()等为它附加回调,另一方面,调用deferred.resolve()或deferred.reject()来肯定或否定这个Promise,且可以向回调传递任意数据。
合并Promise

还记得我前文说的同时发送2个Ajax请求的难题吗?继续以jQuery为例,Promise将可以这样解决它:

var promise1 = $.ajax(url1),
promise2 = $.ajax(url2),
promiseCombined = $.when(promise1, promise2);
promiseCombined.done(onDone);

$.when()方法可以合并多个Promise得到一个新的Promise,相当于在原多个Promise之间建立了AND(逻辑与)的关系,如果所有组成Promise都已成功,则令合并后的Promise也成功,如果有任意一个组成Promise失败,则立即令合并后的Promise失败。
级联Promise

再继续我前文的依次执行一系列异步任务的问题。它将用到Promise最为重要的.then()方法(在Promises/A规范中,也是用“有then()方法的对象”来定义Promise的)。代码如下:

var promise = $.ajax(url1);
promise = promise.then(function(data){
  return $.ajax(url2, data);
});
promise = promise.then(function(data){
  return $.ajax(url3, data);
});
// ...

Promise的.then()方法的完整形式是.then(onDone, onFail, onProgress),这样看上去,它像是一个一次性就可以把各种回调都附加上去的简便方法(.done()、.fail()可以不用了)。没错,你的确可以这样使用,这是等效的。

但.then()方法还有它更为有用的功能。如同then这个单词本身的意义那样,它用来清晰地指明异步事件的前后关系:“先这个,然后(then)再那个”。这称为Promise的级联。

要级联Promise,需要注意的是,在传递给then()的回调函数中,一定要返回你想要的代表下一步任务的Promise(如上面代码的$.ajax(url2, data))。这样,前面被赋值的那个变量才会变成新的Promise。而如果then()的回调函数返回的不是Promise,则then()方法会返回最初的那个Promise。

应该会觉得有些难理解?从代码执行的角度上说,上面这段带有多个then()的代码其实还是被JavaScript引擎运行一遍就结束。但它就像是写好的舞台剧的剧本一样,读过一遍后,JavaScript引擎就会在未来的时刻,依次安排演员按照剧本来演出,而演出都是异步的。then()方法就是让你能写出异步剧本的笔。
将Promise用在基于回调函数的API

前文反复用到的$.ajax()方法会返回一个Promise对象,这其实只是jQuery特意提供的福利。实际情况是,大多数JavaScript API,包括Node.js中的原生函数,都基于回调函数,而不是基于Promise。这种情况下使用Promise会需要自行做一些加工。

这个加工其实比较简单和直接,下面是例子:

var deferred = $.Deferred();
setTimeout(deferred.resolve, 1000);
deferred.done(onDone);

这样,将Promise的肯定或否定的触发器,作为API的回调传入,就变成了Promise的处理模式了。
Promise是怎么实现出来的?

本文写Promise写到这里,你发现了全都是基于已有的实现了Promise的库。那么,如果要自行构筑一个Promise的话呢?

位列于Promises/A的库列表第一位的Q可以算是最符合Promises/A规范且相当直观的实现。如果你想了解如何做出一个Promise,可以参考Q提供的设计模式解析。

限于篇幅,本文只介绍Promise的应用。我会在以后单独开一篇文章来详述Promise的实现细节。

作为JavaScript后续版本的ECMAScript 6将原生提供Promise,如果你想知道它的用法,推荐阅读JavaScript Promises: There and back again。
结语

약속이라는 단어는 너무 완고해서 번역에 적합하지 않고, 얼핏 보면 의미가 불분명해질 것입니다. 그러나 JavaScript에서 더 복잡한 비동기 작업을 수행할 때 실제로 상당한 도움을 줄 수 있습니다.

위 내용은 비동기 JavaScript 프로그래밍_node.js?1.1.5에서의 Promise 사용 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.