更新 (2017)
在 2017 年,Promise 被内置到 JavaScript 中,它们是由 ES2015 规范添加的(polyfills 可用于过时的环境,如 IE8-IE11)。他们使用的语法使用您传递给Promise
构造函数(Promise
执行程序)的回调,该回调接收用于解析/拒绝Promise的函数作为参数。
首先,由于async
now 在 JavaScript 中具有含义(即使在某些上下文中它只是一个关键字),我将使用它later
作为函数的名称以避免混淆。
基本延迟
使用本机Promise(或忠实的 polyfill),它看起来像这样:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
请注意,这假定一个版本setTimeout
符合浏览器的定义,其中setTimeout
不将任何参数传递给回调,除非您在间隔之后提供它们(这在非浏览器环境中可能不是真的,并且过去不是这样)在 Firefox 上是这样,但现在是;在 Chrome 上也是如此,甚至在 IE8 上也是如此)。
带值的基本延迟
如果您希望您的函数有选择地传递一个分辨率值,在任何允许您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));
带值的可取消延迟
如果你想让取消超时成为可能,你不能只从 返回一个Promiselater
,因为Promise不能被取消。
但是我们可以很容易地返回一个带有cancel
方法和访问器的对象,并在取消时拒绝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
}
在对该问题的评论中,我问:
您是否正在尝试创建自己的Promise库?
你说
我不是,但我想现在这实际上是我试图理解的。图书馆将如何做到这一点
为了帮助理解,这里有一个非常非常基本的例子,它不是远程 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>