setTimeoutから約束をする方法


91

これは現実の問題ではなく、約束がどのように作成されるかを理解しようとしているだけです。

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;
}

しかし、これ以上は考えられません。


独自の promiseライブラリを作成しようとしていますか?
TJクラウダー2014年

@TJCrowder私はそうではありませんでしたが、今はそれが実際に理解しようとしていたことだと思います。ライブラリがそれを行う方法
laggingreflex

@遅れ:理にかなっている、私は答えに基本的なプロミス実装の例を追加しました。
TJクラウダー2014年

これは非常に現実的な問題であり、私の会社が構築していた大規模なプロジェクトのために解決しなければならなかった問題だと思います。それを行うにはもっと良い方法があったようですが、私は基本的にBluetoothスタックのために約束の解決を遅らせる必要がありました。以下に投稿して、私がしたことを示します。
晴れ渡った教会の

1
2017年の 'async'は、async function async(){...}
ご存知

回答:


121

アップデート(2017)

ここ2017年、PromiseはJavaScriptに組み込まれ、ES2015仕様によって追加されました(IE8からIE11のような古い環境ではポリフィルが利用可能です)。彼らが行った構文は、引数としてpromiseを解決/拒否するための関数を受け取るPromiseコンストラクター(Promise executor)に渡すコールバックを使用します。

まず、asyncJavaScriptで意味を持つようになったため(特定のコンテキストではキーワードにすぎません)、later混乱を避けるために関数の名前として使用します。

基本遅延

ネイティブプロミス(または忠実なポリフィル)を使用すると、次のようになります。

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

バージョンを前提としていますsetTimeout準拠だとブラウザの定義setTimeout(これは、ブラウザ以外の環境では真ではないかもしれない、となるために使用していないあなたは間隔の後にそれらを与える場合を除き、コールバックに任意の引数を渡すことはありませんがFirefoxではtrueですが、現在は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、Promiseのメソッドとアクセサーを使用してオブジェクトを簡単に返し、キャンセル時にPromiseを拒否できます。

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年の元の回答

通常、promiseライブラリがあります(自分で書いたもの、またはそこにあるいくつかのライブラリの1つ)。そのライブラリには通常、作成して後で「解決」できるオブジェクトがあり、そのオブジェクトには、そこから取得できる「約束」があります。

次に、later次のようになる傾向があります。

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

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

質問に対するコメントで、私は尋ねました:

独自のpromiseライブラリを作成しようとしていますか?

そしてあなたは言った

私はそうではなかったが、今はそれが実際に理解しようとしていたことだと思う。図書館がそれをする方法

その理解を助けるために、リモートでPromises-Aに準拠していない非常に基本的な例を次に示します。ライブコピー

<!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を処理しません
Alexander Danilov

@AlexanderDanilov:約束はキャンセルできません。cancelメソッドを使用してオブジェクトを返す関数と、Promiseのアクセサを個別に作成し、Cancelメソッドが呼び出された場合にPromiseを拒否することは確かにできます...
TJ Crowder

1
@AlexanderDanilov:私は先に進んで1つ追加しました。
TJクラウダー2017年

0

これは元の質問に対する回答ではありません。ただし、元の質問は実際の問題ではないので、問題にはなりません。JavaScriptでのプロミスとは何か、プロミスとコールバックの違いを友人に説明しようとしました。

以下のコードは説明として役立ちます:

//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.