javascript - 如何从 setTimeout 做出 promise

标签 javascript settimeout promise

这不是现实世界的问题,我只是想了解如何创建 promise 。

我需要了解如何为不返回任何内容的函数做出 promise ,例如 setTimeout。

假设我有:

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

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

如何创建 async 可以在 setTimeout 准备好 callback() 后返回的 promise ?

我想包装它会把我带到某个地方:

function setTimeoutReturnPromise(){

    function promise(){}

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

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


    return promise;
}

但我想不出更多。

最佳答案

更新 (2017)

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

首先,自 async now has a meaning in JavaScript (尽管它在某些上下文中只是一个关键字),我将使用 later 作为函数的名称以避免混淆。

基本延迟

使用 native promise (或忠实的 polyfill)它看起来像这样:

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

请注意,这里假定 setTimeout 的版本符合 the definition for browsers其中 setTimeout 不会将任何参数传递给回调,除非您在间隔之后给它们(这在非浏览器环境中可能不是真的,并且在 Firefox 上过去不是这样,但现在是现在;在 Chrome 上是这样,甚至在 IE8 上也是如此。

具有值(value)的基本延迟

如果您希望您的函数有选择地传递一个分辨率值,在任何允许您在延迟后向 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 返回一个 promise,因为 promise 不能被取消。

但我们可以很容易地返回一个带有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 库(您自己编写的一个,或者其他几个之一)。该库通常会有一个您可以创建并稍后“解析”的对象,并且该对象会有一个您可以从中获得的“ promise ”。

然后 later 看起来像这样:

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

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

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

Are you trying to create your own promise library?

你说

I wasn't but I guess now that's actually what I was trying to understand. That how a library would do it

为了帮助理解,这里有一个非常非常基本的例子,它远不符合 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>

关于javascript - 如何从 setTimeout 做出 promise ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22707475/

相关文章:

javascript - 为什么这段代码在 setTimeout 中调用时有效,但在其外部无效?

javascript - 吞下消息 : Error: Uncaught (in promise): [object Undefined]

javascript - html5 Canvas 加载圆圈图像

javascript - 在没有 jQuery 的情况下在 node.js 上合并或合并 JSON

javascript - 如何消除 ES6 导出语句中的 eslint 解析错误

javascript - 如何从 promise 中获得结果

angularjs - 在 $routeProvider 解析中返回相互依赖的异步 promise

javascript - 在 JS 中键入时替换输入值

javascript - 如何识别html页面中的所有元素都已经渲染完毕?

javascript - 使用 Javascript 打开一个新页面并在那里填充表单值