SetTimeout'tan nasıl söz verilir


96

Bu gerçek bir dünya sorunu değil, sadece vaatlerin nasıl yaratıldığını anlamaya çalışıyorum.

SetTimeout gibi hiçbir şey döndürmeyen bir işlev için nasıl söz verileceğini anlamam gerekiyor.

Varsayalım ki:

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

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

Hazır asyncolduktan sonra geri dönebilecek bir sözü nasıl oluşturabilirim ?setTimeoutcallback()

Sarmanın beni bir yere götüreceğini düşünüyordum:

function setTimeoutReturnPromise(){

    function promise(){}

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

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


    return promise;
}

Ama bunun ötesinde düşünemiyorum.


Kendi söz kitaplığınızı oluşturmaya mı çalışıyorsunuz ?
TJ Crowder

@TJCrowder Değildim ama sanırım şimdi anlamaya çalıştığım şey buydu. Bir kütüphane bunu nasıl yapardı
laggingreflex

@ lagging: Mantıklı, cevaba örnek bir temel vaat uygulaması ekledim.
TJ Crowder

Bence bu çok gerçek bir problem ve şirketimin inşa etmekte olduğu büyük bir proje için çözmem gereken bir problem. Bunu yapmanın muhtemelen daha iyi yolları vardı, ancak esasen bluetooth yığınımız uğruna bir sözün çözümünü ertelemem gerekiyordu. Ne yaptığımı göstermek için aşağıya göndereceğim.
sunny-mittal

1
2017 'zaman uyumsuz' de biraz kafa karıştırıcı adı aklınıza gelebilecek gibi bir işlev için ise, sadece Notasync function async(){...}
mikemaccana

Yanıtlar:


132

Güncelleme (2017)

Burada 2017'de, Promises JavaScript'te yerleşiktir, ES2015 spesifikasyonu tarafından eklenmiştir (çoklu dolgular, IE8-IE11 gibi eski ortamlar için mevcuttur). Birlikte gittikleri sözdizimi Promise, Promise yapıcıya ( yürütücü ) ilettiğiniz ve sözün bağımsız değişken olarak çözümlenmesi / reddedilmesi için işlevleri alan bir geri aramayı kullanır .

Öncelikle, asyncartık JavaScript'te bir anlamı olduğundan (belirli bağlamlarda yalnızca bir anahtar kelime olsa da), laterkarışıklığı önlemek için işlevin adı olarak kullanacağım .

Temel Gecikme

Yerel vaatler (veya sadık bir çoklu doldurma) kullanmak şöyle görünür:

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

Bunun bir versiyonu varsayar Not setTimeoutbununla uyumlu olduğunu tarayıcılar için tanım nerede setTimeoutbu tarayıcı olmayan ortamlarda doğru olmayabilir (aradan sonra bunları vermedikçe geri arama için herhangi bir bağımsız değişken geçmez ve eskiden yoktu Firefox'ta doğru, ancak şimdi; Chrome'da ve hatta IE8'de doğrudur).

Değerli Temel Gecikme

İşlevinizin isteğe bağlı olarak setTimeout, gecikmeden sonra ekstra argümanlar vermenize izin veren ve daha sonra bunları çağrıldığında geri aramaya aktaran herhangi bir belirsiz modern tarayıcıda bir çözünürlük değeri iletmesini istiyorsanız , bunu yapabilirsiniz (mevcut Firefox ve Chrome; IE11 + , muhtemelen Edge; IE8 veya IE9 değil , IE10 hakkında hiçbir fikrim yok):

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 + ok işlevlerini kullanıyorsanız, bu daha kısa olabilir:

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

ya da

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

Değerli İptal Edilebilir Gecikme

Zaman aşımını iptal etmeyi mümkün kılmak istiyorsanız, sözünü geri laterveremezsiniz çünkü sözler iptal edilemez.

Ancak, bir cancelyöntem ve erişimci içeren bir nesneyi söz için kolayca iade edebilir ve iptal etme sözünü reddedebiliriz:

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

Canlı Örnek:

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 Tarafından Verilen Orijinal Cevap

Genellikle bir söz kitaplığınız olur (birini kendiniz yazarsınız veya oradaki birkaç kitaptan biri). Bu kitaplık genellikle oluşturabileceğiniz ve daha sonra "çözebileceğiniz" bir nesneye sahip olacak ve bu nesnenin ondan alabileceğiniz bir "sözü" olacaktır.

O zaman laterşuna benzer bir şeye bakma eğilimindedir:

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

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

Soruya bir yorumda sordum:

Kendi söz kitaplığınızı oluşturmaya mı çalışıyorsunuz?

ve sen dedin

Değildim ama sanırım şimdi anlamaya çalıştığım şey buydu. Bir kütüphane bunu nasıl yapardı

Bu anlayışa yardımcı olmak için işte çok basit bir örnek, uzaktan Promises-A uyumlu olmayan: Canlı Kopya

<!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>


cevabınız cancelTimeout'u karşılamıyor
Alexander Danilov

@AlexanderDanilov: Sözler iptal edilemez. Kesinlikle iptal yöntemiyle bir nesneyi döndüren bir işlev ve ayrı olarak söz için bir erişimci yazabilir, ardından iptal yöntemi çağrıldıysa sözü reddedebilirsiniz ...
TJ Crowder

1
@AlexanderDanilov: Devam ettim ve bir tane ekledim.
TJ Crowder

1
const setTimeoutAsync = (cb, delay) =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(cb());
    }, delay);
  });

Bunun gibi özel 'cb fxn' geçirebiliriz 👆🏽


0

Bu, orijinal sorunun cevabı değil. Ancak, orijinal bir soru gerçek dünya sorunu olmadığı için sorun olmamalıdır. Bir arkadaşıma JavaScript'te vaatlerin ne olduğunu ve vaat ile geri arama arasındaki farkı açıklamaya çalıştım.

Aşağıdaki kod bir açıklama görevi görür:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.