这不是一个现实世界的问题,我只想了解如何创造承诺。
我需要了解如何为一个不返回任何内容的函数做出承诺,比如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;
}
但除此之外,我无法思考。
答案 0 :(得分:75)
2017年,Promises内置于JavaScript中,它们是由ES2015规范添加的(polyfill适用于过时的环境,如IE8-IE11)。他们使用的语法使用您传递给Promise
构造函数(Promise
执行程序)的回调函数,该函数接收解析/拒绝作为参数的promise的函数。
首先,因为async
now has a meaning in JavaScript(即使它只是某些上下文中的关键字),我将使用later
作为函数的名称以避免混淆。
使用原生承诺(或忠实的polyfill),它看起来像这样:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
请注意,假设setTimeout
的版本符合the definition for browsers,其中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));
如果您希望取消超时,则不能仅从later
返回承诺,因为承诺无法取消。
但是我们可以使用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);
通常你会有一个诺言库(你自己写的,或者其中一个)。该库通常会有一个您可以创建并稍后“解析”的对象,并且该对象将具有您可以从中获得的“承诺”。
然后later
会看起来像这样:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
在对这个问题的评论中,我问道:
您是否正在尝试创建自己的承诺库?
你说
我不是,但我想现在这实际上是我想要了解的内容。图书馆将如何做到这一点
为了帮助理解,这里是非常非常基本的示例,它不符合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>
答案 1 :(得分:1)
const setTimeoutAsync = (cb, delay) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(cb());
}, delay);
});
我们可以像这样通过自定义'cb fxn'??
答案 2 :(得分:0)
这不是原始问题的答案。但是,由于最初的问题不是现实问题,因此不应该成为问题。我试图向朋友解释JavaScript中的promise是什么,promise和callback之间的区别。
以下代码用作说明:
//very basic callback example using setTimeout
//function a is asynchronous function
//function b used as a callback
function a (callback){
setTimeout (function(){
console.log ('using callback:');
let mockResponseData = '{"data": "something for callback"}';
if (callback){
callback (mockResponseData);
}
}, 2000);
}
function b (dataJson) {
let dataObject = JSON.parse (dataJson);
console.log (dataObject.data);
}
a (b);
//rewriting above code using Promise
//function c is asynchronous function
function c () {
return new Promise(function (resolve, reject) {
setTimeout (function(){
console.log ('using promise:');
let mockResponseData = '{"data": "something for promise"}';
resolve(mockResponseData);
}, 2000);
});
}
c().then (b);
答案 3 :(得分:0)
实施:
// Promisify setTimeout
const pause = (ms, cb, ...args) =>
new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await cb?.(...args))
} catch (error) {
reject(error)
}
}, ms)
})
测试:
// Test 1
pause(1000).then(() => console.log('called'))
// Test 2
pause(1000, (a, b, c) => [a, b, c], 1, 2, 3).then(value => console.log(value))
// Test 3
pause(1000, () => {
throw Error('foo')
}).catch(error => console.error(error))