setTimeout에서 약속을 만드는 방법


96

이것은 실제 문제가 아닙니다. 저는 약속이 어떻게 생성되는지 이해하려고 노력하고 있습니다.

setTimeout처럼 아무것도 반환하지 않는 함수에 대한 약속을 만드는 방법을 이해해야합니다.

내가 가지고 있다고 가정합니다.

function async(callback){ 
    setTimeout(function(){
        callback();
    }, 5000);
}

async(function(){
    console.log('async called back');
});

준비가 완료된 async후 반환 할 수 있는 프라 미스를 어떻게 생성 합니까?setTimeoutcallback()

포장하면 어딘가에 걸릴 것이라고 생각했습니다.

function setTimeoutReturnPromise(){

    function promise(){}

    promise.prototype.then = function() {
        console.log('timed out');
    };

    setTimeout(function(){
        return ???
    },2000);


    return promise;
}

하지만이 이상은 생각할 수 없습니다.


자신 만의 약속 라이브러리 를 만들려고 합니까?
TJ Crowder

@TJCrowder 나는 아니었지만 지금은 실제로 내가 이해하려고했던 것 같아요. 라이브러리가 그것을 얼마나 그
laggingreflex

@ lagging : 말이됩니다. 기본 약속 구현 예제를 답변에 추가했습니다.
TJ Crowder

나는 이것이 매우 실제적인 문제라고 생각하며 우리 회사가 구축하고있는 대규모 프로젝트를 위해 해결해야하는 문제입니다. 더 나은 방법이 있었을 가능성이 있지만 본질적으로 블루투스 스택을 위해 promise의 해상도를 지연해야했습니다. 내가 한 일을 보여주기 위해 아래에 게시하겠습니다.
sunny-mittal

1
2017 '비동기'에 다소 혼란 이름은 당신이 줄 수 있으므로, 함수에 대한 것을 그냥 메모async function async(){...}
mikemaccana

답변:


129

업데이트 (2017)

2017 년에는 Promise가 JavaScript에 내장되어 ES2015 사양에 의해 추가되었습니다 (폴리 필은 IE8-IE11과 같은 오래된 환경에서 사용 가능). 그들이 사용했던 구문은 당신이 Promise생성자 ( Promise executor )에 전달하는 콜백을 사용합니다.이 콜백 은 인자로 프라 미스를 해결 / 거부하는 함수를받습니다.

첫째, async이제 JavaScript에서 의미가 있기 때문에 (특정 컨텍스트에서는 키워드 일뿐 임에도 불구하고) later혼란을 피하기 위해 함수의 이름으로 사용하겠습니다 .

기본 지연

네이티브 프라 미스 (또는 충실한 폴리 필)를 사용하면 다음과 같습니다.

function later(delay) {
    return new Promise(function(resolve) {
        setTimeout(resolve, delay);
    });
}

그 버전 가정합니다 setTimeout그 준수의 브라우저에 대한 정의setTimeout이 비 브라우저 환경에서 진실하지 않을 수 있습니다 (간격 후에을 제공하지 않는 콜백에 인수를 전달하지 않으며,로 사용하지 않았다 Firefox에서는 사실이지만 지금은 그렇습니다. Chrome에서는 사실이며 IE8에서도 마찬가지입니다.)

가치가있는 기본 지연

함수가 선택적으로 해상도 값을 전달하도록 setTimeout하려면 지연 후 추가 인수를 제공 한 다음 호출시 콜백에 전달할 수있는 모호한 최신 브라우저에서이를 수행 할 수 있습니다 (현재 Firefox 및 Chrome, IE11 + , 아마도 Edge; IE8 또는 IE9가 아니라 IE10에 대한 정보 없음) :

function later(delay, value) {
    return new Promise(function(resolve) {
        setTimeout(resolve, delay, value); // Note the order, `delay` before `value`
        /* Or for outdated browsers that don't support doing that:
        setTimeout(function() {
            resolve(value);
        }, delay);
        Or alternately:
        setTimeout(resolve.bind(null, value), delay);
        */
    });
}

ES2015 + 화살표 기능을 사용하는 경우 더 간결 할 수 있습니다.

function later(delay, value) {
    return new Promise(resolve => setTimeout(resolve, delay, value));
}

또는

const later = (delay, value) =>
    new Promise(resolve => setTimeout(resolve, delay, value));

값이있는 취소 가능한 지연

시간 제한을 취소 할 수 있도록하려면 약속을 later취소 할 수 없기 때문에 에서 약속을 반환 할 수 없습니다.

그러나 우리는 프라 미스에 대한 cancel메소드와 접근자를 가진 객체를 쉽게 반환 할 수 있으며 취소시 프라 미스를 거부 할 수 있습니다.

const later = (delay, value) => {
    let timer = 0;
    let reject = null;
    const promise = new Promise((resolve, _reject) => {
        reject = _reject;
        timer = setTimeout(resolve, delay, value);
    });
    return {
        get promise() { return promise; },
        cancel() {
            if (timer) {
                clearTimeout(timer);
                timer = 0;
                reject();
                reject = null;
            }
        }
    };
};

라이브 예 :

const later = (delay, value) => {
    let timer = 0;
    let reject = null;
    const promise = new Promise((resolve, _reject) => {
        reject = _reject;
        timer = setTimeout(resolve, delay, value);
    });
    return {
        get promise() { return promise; },
        cancel() {
            if (timer) {
                clearTimeout(timer);
                timer = 0;
                reject();
                reject = null;
            }
        }
    };
};

const l1 = later(100, "l1");
l1.promise
  .then(msg => { console.log(msg); })
  .catch(() => { console.log("l1 cancelled"); });

const l2 = later(200, "l2");
l2.promise
  .then(msg => { console.log(msg); })
  .catch(() => { console.log("l2 cancelled"); });
setTimeout(() => {
  l2.cancel();
}, 150);


2014 년의 원래 답변

일반적으로 약속 라이브러리가 있습니다 (하나는 직접 작성하거나 여러 개 중 하나). 해당 라이브러리에는 일반적으로 생성하고 나중에 "해결"할 수있는 객체가 있으며, 해당 객체에는 얻을 수있는 "약속"이 있습니다.

그러면 다음 later과 같은 경향이 있습니다.

function later() {
    var p = new PromiseThingy();
    setTimeout(function() {
        p.resolve();
    }, 2000);

    return p.promise(); // Note we're not returning `p` directly
}

질문에 대한 의견에서 다음과 같이 질문했습니다.

자신 만의 약속 라이브러리를 만들려고합니까?

그리고 당신은 말했다

나는 아니었지만 이제는 그것이 실제로 내가 이해하려고 노력한 것 같습니다. 도서관이하는 방법

이러한 이해를 돕기 위해 원격으로 Promises-A를 준수하지 않는 매우 기본적인 예가 있습니다. Live Copy

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
  <script>
    (function() {

      // ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
      var PromiseThingy = (function() {

        // Internal - trigger a callback
        function triggerCallback(callback, promise) {
          try {
            callback(promise.resolvedValue);
          }
          catch (e) {
          }
        }

        // The internal promise constructor, we don't share this
        function Promise() {
          this.callbacks = [];
        }

        // Register a 'then' callback
        Promise.prototype.then = function(callback) {
          var thispromise = this;

          if (!this.resolved) {
            // Not resolved yet, remember the callback
            this.callbacks.push(callback);
          }
          else {
            // Resolved; trigger callback right away, but always async
            setTimeout(function() {
              triggerCallback(callback, thispromise);
            }, 0);
          }
          return this;
        };

        // Our public constructor for PromiseThingys
        function PromiseThingy() {
          this.p = new Promise();
        }

        // Resolve our underlying promise
        PromiseThingy.prototype.resolve = function(value) {
          var n;

          if (!this.p.resolved) {
            this.p.resolved = true;
            this.p.resolvedValue = value;
            for (n = 0; n < this.p.callbacks.length; ++n) {
              triggerCallback(this.p.callbacks[n], this.p);
            }
          }
        };

        // Get our underlying promise
        PromiseThingy.prototype.promise = function() {
          return this.p;
        };

        // Export public
        return PromiseThingy;
      })();

      // ==== Using it

      function later() {
        var p = new PromiseThingy();
        setTimeout(function() {
          p.resolve();
        }, 2000);

        return p.promise(); // Note we're not returning `p` directly
      }

      display("Start " + Date.now());
      later().then(function() {
        display("Done1 " + Date.now());
      }).then(function() {
        display("Done2 " + Date.now());
      });

      function display(msg) {
        var p = document.createElement('p');
        p.innerHTML = String(msg);
        document.body.appendChild(p);
      }
    })();
  </script>
</body>
</html>


당신의 대답은 cancelTimeout 처리하지 않습니다
알렉산더 다닐 로프

@AlexanderDanilov : 약속은 취소 할 수 없습니다. 취소 메서드가 호출 된 경우 확실히 약속에 대해 개별적으로 접근을 방법을 취소하고와 객체를 반환하는 함수를 작성할 수, 다음 ... 약속을 거부
TJ 크라우

1
@AlexanderDanilov : 계속해서 추가했습니다.
TJ Crowder

0

이것은 원래 질문에 대한 답이 아닙니다. 그러나 원래의 질문은 실제 문제가 아니므로 문제가되지 않아야합니다. 나는 친구에게 자바 스크립트의 프라 미스와 프라 미스와 콜백의 차이점을 설명하려고 노력했다.

아래 코드는 설명으로 사용됩니다.

//very basic callback example using setTimeout
//function a is asynchronous function
//function b used as a callback
function a (callback){
    setTimeout (function(){
       console.log ('using callback:'); 
       let mockResponseData = '{"data": "something for callback"}'; 
       if (callback){
          callback (mockResponseData);
       }
    }, 2000);

} 

function b (dataJson) {
   let dataObject = JSON.parse (dataJson);
   console.log (dataObject.data);   
}

a (b);

//rewriting above code using Promise
//function c is asynchronous function
function c () {
   return new Promise(function (resolve, reject) {
     setTimeout (function(){
       console.log ('using promise:'); 
       let mockResponseData = '{"data": "something for promise"}'; 
       resolve(mockResponseData); 
    }, 2000);      
   }); 

}

c().then (b);

JsFiddle

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.