我正在尝试为返回promise的对象运行测试套件。我希望将几个动作连在一起,并在它们之间进行短暂的超时。我认为那是"然后"在发出下一个链接然后调用之前,返回承诺的调用将等待承诺完成。
我创建了一个函数
function promiseTimeout (time) {
return new Promise(function(resolve,reject){
setTimeout(function(){resolve(time);},time);
});
};
尝试在承诺中包装setTimeout。
然后在我的测试套件中,我正在调用这样的东西......
it('should restore state when browser back button is used',function(done){
r.domOK().then(function(){
xh.fire('akc-route-change','/user/4/profile/new');
}).then(promiseTimeout(2000)).then(function(t){
xu.fire('akc-route-change','/user/6');
}).then(promiseTimeout(10)).then(function(t){
expect(xu.params[0]).to.equal(6);
history.back();
}).then(promiseTimeout(10)).then(function(){
expect(xu.params[0]).to.equal(4);
done();
});
});
我可以在第xh.fire
次呼叫上设置断点,在xu.fire
呼叫上设置第二个断点,并且当从第一个断点继续到第二个断点时,预计会有两秒的间隙。
而是立即到达第二个断点,此时t
的值未定义。
我做错了什么?
答案 0 :(得分:15)
TL; DR - 你已经在一个承诺中正确包装了setTimeout,问题是你使用它不正确
.then(promiseTimeout(2000)).then
不会做你期望的。 .then的“签名”是then(functionResolved, functionRejected)
promise的then方法接受两个参数:
promise.then(onFulfilled,onRejected)
onFulfilled和onRejected都是可选参数:
- 如果onFulfilled不是函数,则必须忽略它。
- 如果onRejected不是函数,则必须忽略它。
来源:https://promisesaplus.com/#point-21
您没有将功能传递给
考虑一下你的做法:
Promise.resolve('hello')
.then(promiseTimeout(2000))
.then(console.log.bind(console))
vs应如何做:
Promise.resolve('hello').then(function() {
return promiseTimeout(2000)
}).then(console.log.bind(console))
第一个输出'你好'立即
第二次输出2000秒后
因此,您应该这样做:
it('should restore state when browser back button is used', function(done) {
r.domOK().then(function() {
xh.fire('akc-route-change', '/user/4/profile/new');
}).then(function() {
return promiseTimeout(2000);
}).then(function(t) {
xu.fire('akc-route-change', '/user/6');
}).then(function() {
return promiseTimeout(10);
}).then(function(t) {
expect(xu.params[0]).to.equal(6);
history.back();
}).then(function() {
return promiseTimeout(10);
}).then(function() {
expect(xu.params[0]).to.equal(4);
done();
});
});
可替换地:
it('should restore state when browser back button is used', function(done) {
r.domOK().then(function() {
xh.fire('akc-route-change', '/user/4/profile/new');
}).then(promiseTimeout.bind(null, 2000)
).then(function(t) {
xu.fire('akc-route-change', '/user/6');
}).then(promiseTimeout.bind(null, 10)
).then(function(t) {
expect(xu.params[0]).to.equal(6);
history.back();
}).then(promiseTimeout.bind(null, 10)
).then(function() {
expect(xu.params[0]).to.equal(4);
done();
});
});
多年来,情况发生了很大变化 - 箭头符号使这更容易
首先,我将以不同的方式定义promiseTimeout
const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time, time));
上面返回一个函数,可以调用该函数来创建“promise delay”并解析为时间(延迟的长度)。考虑到这一点,我不明白为什么那会非常有用,而不是我:
const promiseTimeout = time => result => new Promise(resolve => setTimeout(resolve, time, result));
以上将解决前一个承诺的结果(更有用)
但它是一个返回函数的函数,因此ORIGINAL代码的其余部分可以保持不变。然而,关于原始代码的事情是,不需要在.then链中传递任何值,因此,甚至更简单
const promiseTimeout = time => () => new Promise(resolve => setTimeout(resolve, time));
现在可以使用问题的it
块中的原始代码不变
it('should restore state when browser back button is used',function(done){
r.domOK().then(function(){
xh.fire('akc-route-change','/user/4/profile/new');
}).then(promiseTimeout(2000)).then(function(){
xu.fire('akc-route-change','/user/6');
}).then(promiseTimeout(10)).then(function(){
expect(xu.params[0]).to.equal(6);
history.back();
}).then(promiseTimeout(10)).then(function(){
expect(xu.params[0]).to.equal(4);
done();
});
});
答案 1 :(得分:5)
要使超时工作正常,请编写一个延迟的函数,并返回一个适合传递给then
的函数。
function timeout(ms) {
return () => new Promise(resolve => setTimeout(resolve, ms));
}
像这样使用:
Promise.resolve() . then(timeout(1000)) . then(() => console.log("got here"););
但是,您可能希望访问导致超时的promise的已解析值。在这种情况下,安排由timeout()
创建的函数来传递值:
function timeout(ms) {
return value => new Promise(resolve => setTimeout(() => resolve(value), ms));
}
像这样使用:
Promise.resolve(42) . then(timeout(1000)) . then(value => console.log(value));
答案 2 :(得分:4)
await new Promise((resolve, reject)=>{
// wait for 50 ms.
setTimeout(function(){resolve()}, 50);
});
console.log("This will appear after waiting for 50 ms");
这可以在异步函数中使用,执行将等到给定的间隔。
答案 3 :(得分:1)
正如上面已经回答的那样,但是我认为可以轻松地做到这一点:
const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms))
setTimeoutProise
函数在ms
中接受等待时间,并将其传递给setTimeout
函数。等待时间结束后,将执行传递给promise的resolve方法。
可以这样使用:
setTimeoutPromise(3000).then(doSomething)
答案 4 :(得分:0)
另一种在没有预先定义或Promise
辅助功能(我个人最喜欢)的情况下为import
添加延迟的方法是扩展{ {1}}构造函数:
Promise
我将省略Promise.prototype.delay = function (ms) {
return new Promise(resolve => {
window.setTimeout(this.then.bind(this, resolve), ms);
});
}
回调,因为这意味着总是reject
。
演示
resolve
Promise.prototype.delay = function(ms) {
console.log(`[log] Fetching data in ${ms / 1000} second(s)...`);
return new Promise(resolve => {
window.setTimeout(this.then.bind(this, resolve), ms);
});
}
document.getElementById('fetch').onclick = function() {
const duration = 1000 * document.querySelector('#duration[type="number"]').value;
// Promise.resolve() returns a Promise
// and this simply simulates some long-running background processes
// so we can add some delays on it
Promise
.resolve('Some data from server.')
.delay(duration)
.then(console.log);
}
或者如果您希望它也可以<div>
<input id="duration" type="number" value="3" />
<button id="fetch">Fetch data from server</button>
</div>
可用,这就是您需要第二个参数(.catch()
)的时候。请注意,reject
处理也会在延迟之后发生:
catch()
Promise.prototype.delay = function(ms) {
console.log(`[log] Fetching data in ${ms / 1000} second(s)...`);
return new Promise((resolve, reject) => {
window.setTimeout(() => {
this.then(resolve).catch(reject);
}, ms);
});
}
document.getElementById('fetch').onclick = function() {
const duration = 1000 * document.querySelector('#duration[type="number"]').value;
Promise
.reject('Some data from server.')
.delay(duration)
.then(console.log)
.catch(console.log); // Promise rejection or failures will always end up here
}