Cara membuat janji dari setTimeout

96

Ini bukan masalah dunia nyata, saya hanya mencoba memahami bagaimana janji dibuat.

Saya perlu memahami cara membuat janji untuk fungsi yang tidak mengembalikan apa-apa, seperti setTimeout.

Misalkan saya memiliki:

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

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

Bagaimana cara membuat janji yang asyncdapat dikembalikan setelah setTimeoutsiap callback()?

Saya kira membungkusnya akan membawa saya ke suatu tempat:

function setTimeoutReturnPromise(){

    function promise(){}

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

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


    return promise;
}

Tapi saya tidak bisa berpikir lebih jauh dari ini.

refleks tertinggal
sumber
Apakah Anda mencoba membuat pustaka janji Anda sendiri ?
TJ Crowder
@TJCrowder Saya tidak, tapi saya rasa sekarang itulah yang sebenarnya saya coba pahami. Begitulah cara perpustakaan melakukannya
laggingreflex
@ Lagging: Masuk akal, saya telah menambahkan contoh implementasi janji dasar ke jawabannya.
TJ Crowder
Saya pikir ini adalah masalah dunia nyata dan yang harus saya selesaikan untuk proyek besar yang sedang dibangun perusahaan saya. Kemungkinan ada cara yang lebih baik untuk melakukannya, tetapi pada dasarnya saya perlu menunda resolusi janji demi tumpukan bluetooth kami. Saya akan posting di bawah ini untuk menunjukkan apa yang saya lakukan.
sunny-mittal
1
Sekadar catatan bahwa di 2017 'async' adalah nama yang agak membingungkan untuk suatu fungsi, seperti yang mungkin Anda milikiasync function async(){...}
mikemaccana

Jawaban:

132

Pembaruan (2017)

Di sini, di tahun 2017, Promises dibuat ke dalam JavaScript, dan ditambahkan oleh spesifikasi ES2015 (polyfill tersedia untuk lingkungan lama seperti IE8-IE11). Sintaks yang mereka gunakan menggunakan callback yang Anda berikan ke Promisekonstruktor ( Promise pelaksana ) yang menerima fungsi untuk menyelesaikan / menolak janji sebagai argumen.

Pertama, karena asyncsekarang memiliki arti dalam JavaScript (meskipun itu hanya kata kunci dalam konteks tertentu), saya akan menggunakan laternama fungsinya untuk menghindari kebingungan.

Penundaan Dasar

Menggunakan janji asli (atau polyfill yang setia) akan terlihat seperti ini:

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

Perhatikan bahwa itu mengasumsikan versi setTimeoutyang sesuai dengan definisi untuk browser di mana setTimeouttidak meneruskan argumen apa pun ke callback kecuali Anda memberikannya setelah interval (ini mungkin tidak benar di lingkungan non-browser, dan tidak dulu benar di Firefox, tetapi sekarang; itu benar di Chrome dan bahkan di IE8).

Penundaan Dasar dengan Nilai

Jika Anda ingin fungsi Anda meneruskan nilai resolusi secara opsional, pada browser modern apa pun yang memungkinkan Anda memberikan argumen tambahan setTimeoutsetelah penundaan dan kemudian meneruskannya ke callback saat dipanggil, Anda dapat melakukan ini (Firefox dan Chrome saat ini; IE11 + , mungkin Edge; bukan IE8 atau IE9, tidak tahu tentang 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);
        */
    });
}

Jika Anda menggunakan fungsi panah ES2015 +, itu bisa lebih ringkas:

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

atau bahkan

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

Penundaan yang Dapat Dibatalkan dengan Nilai

Jika Anda ingin memungkinkan untuk membatalkan waktu tunggu, Anda tidak bisa hanya mengembalikan janji dari later, karena janji tidak bisa dibatalkan.

Tapi kita bisa dengan mudah mengembalikan objek dengan cancelmetode dan aksesor untuk promise, dan menolak promise saat pembatalan:

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

Contoh Langsung:

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


Jawaban Asli dari 2014

Biasanya Anda akan memiliki perpustakaan promise (yang Anda tulis sendiri, atau salah satu dari beberapa di luar sana). Perpustakaan itu biasanya akan memiliki objek yang bisa Anda buat dan kemudian "diselesaikan", dan objek itu akan memiliki "janji" yang bisa Anda dapatkan darinya.

Maka laterakan cenderung terlihat seperti ini:

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

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

Dalam komentar atas pertanyaan itu, saya bertanya:

Apakah Anda mencoba membuat pustaka janji Anda sendiri?

dan kamu berkata

Saya tidak, tetapi saya rasa sekarang itulah yang sebenarnya saya coba pahami. Begitulah cara perpustakaan melakukannya

Untuk membantu pemahaman itu, berikut adalah contoh yang sangat mendasar , yang tidak sesuai dengan Promises-A dari jarak jauh: 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>
TJ Crowder
sumber
modernjavascript.blogspot.com/2013/08/… terkait :)
Benjamin Gruenbaum
jawaban Anda tidak menangani cancelTimeout
Alexander Danilov
@AlexanderDanilov: Janji tidak dapat dibatalkan. Anda tentu bisa menulis fungsi yang mengembalikan objek dengan metode pembatalan dan, secara terpisah, pengakses untuk promise, lalu menolak promise jika metode pembatalan dipanggil ...
TJ Crowder
1
@AlexanderDanilov: Saya melanjutkan dan menambahkan satu.
TJ Crowder
1
const setTimeoutAsync = (cb, delay) =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(cb());
    }, delay);
  });

Kita bisa lewat custom 'cb fxn' seperti ini 👆🏽

CodeFinity
sumber
0

Ini bukanlah jawaban dari pertanyaan awal. Tapi, karena pertanyaan orisinal bukanlah masalah dunia nyata, ini seharusnya tidak menjadi masalah. Saya mencoba menjelaskan kepada teman apa saja yang menjadi promise dalam JavaScript dan perbedaan antara promise dan callback.

Kode di bawah ini berfungsi sebagai penjelasan:

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

yurin
sumber