更新(2017年)
在2017年,Promise已经内置于JavaScript中,它们是由ES2015规范添加的(对于像IE8-IE11这样的过时环境,可以使用polyfill)。他们选择的语法使用一个回调函数传递给Promise
构造函数(Promise
executor),该回调函数接收用于解析/拒绝承诺的函数作为参数。
首先,由于现在在JavaScript中有一个含义的async
(尽管它只是在某些上下文中的关键字),我将使用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);
});
}
如果你使用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 年的原始答案
通常情况下,你会有一个承诺库(可以是自己编写的,也可以是市面上的几个库之一)。该库通常会有一个对象,你可以创建它并稍后进行“解决”,该对象将具有你可以从中获取“承诺”的“承诺”。
然后,later
往往会看起来像这样:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise();
}
在问题的评论中,我问道:
你是想创建自己的Promise库吗?
而你回答说:
我本来不是这样想的,但现在我想其实我是在尝试理解如何创建一个库。
为了帮助你更好地理解,以下是一个非常基本的例子,它并不符合Promises-A规范:Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
var PromiseThingy = (function() {
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
function Promise() {
this.callbacks = [];
}
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
this.callbacks.push(callback);
}
else {
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
function PromiseThingy() {
this.p = new 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);
}
}
};
PromiseThingy.prototype.promise = function() {
return this.p;
};
return PromiseThingy;
})();
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise();
}
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>
async function async(){...}
。 - mikemaccanaasync
重命名为later
并相应地编辑已接受的答案,以避免任何混淆,对于未来的读者是否有益呢? - Sebastian Simon