这不是现实问题,我只是想了解如何创建承诺。
我需要了解如何对不返回任何内容的函数(例如setTimeout)作出承诺。
假设我有:
function async(callback){ setTimeout(function(){ callback(); }, 5000); } async(function(){ console.log('async called back'); });
如何创建async可以在setTimeout准备就绪后返回的承诺callback()?
async
setTimeout
callback()
我本来应该把它包起来带到某个地方:
function setTimeoutReturnPromise(){ function promise(){} promise.prototype.then = function() { console.log('timed out'); }; setTimeout(function(){ return ??? },2000); return promise; }
但是我无法超越这个范围。
在2017年,Promises内置在JavaScript中,并由ES2015规范添加(polyfill可用于IE8-IE11等过时的环境)。他们使用的语法使用传递给Promise构造函数(Promiseexecutor)的回调,该回调函数接收用于解析/拒绝promise的函数作为参数。
Promise
首先,由于async现在在JavaScript中具有含义(即使在某些情况下只是一个关键字),我将使用它later作为函数的名称以避免混淆。
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承诺的方法和访问器返回对象,并在取消时拒绝承诺:
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);
通常,您将拥有一个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>