一尘不染

如何通过setTimeout做出承诺

javascript

这不是现实问题,我只是想了解如何创建承诺。

我需要了解如何对不返回任何内容的函数(例如setTimeout)作出承诺。

假设我有:

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

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

如何创建async可以在setTimeout准备就绪后返回的承诺callback()

我本来应该把它包起来带到某个地方:

function setTimeoutReturnPromise(){

    function promise(){}

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

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


    return promise;
}

但是我无法超越这个范围。


阅读 296

收藏
2020-05-01

共1个答案

一尘不染

更新

在2017年,Promises内置在JavaScript中,并由ES2015规范添加(polyfill可用于IE8-IE11等过时的环境)。他们使用的语法使用传递给Promise构造函数(Promiseexecutor)的回调,该回调函数接收用于解析/拒绝promise的函数作为参数。

首先,由于async现在在JavaScript中具有含义(即使在某些情况下只是一个关键字),我将使用它later作为函数的名称以避免混淆。

基本延迟

使用本机的Promise(或忠实的polyfill)看起来像这样:

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

需要注意的是,它假定的一个版本,setTimeout这是符合该定义的浏览器,其中setTimeout不传递任何参数的回调,除非你给他们的时间间隔后(这可能不是在非浏览器环境中真实的,并没有曾经是在Firefox上为true,但现在是;在Chrome上甚至在IE8上都为true)。

具有价值的基本延迟

如果您希望函数有选择地传递分辨率值,请在任何模糊的现代浏览器上允许您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年的原始答案

通常,您将拥有一个Promise库(一个是您自己编写的,或者是其中几个库中的一个)。该库通常会有一个您可以创建并稍后“解析”的对象,而该对象将有一个您可以从中获得的“承诺”。

然后later往往看起来像这样:

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

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

在对问题的评论中,我问:

您是否正在尝试创建自己的Promise库?

你说

我不是,但我现在想这就是我想要了解的内容。那图书馆怎么做

<!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>
2020-05-01