I have the following promise, but it seems to resolve without me even awaiting it if I run this code in ts playground.
const promises = [later(1000)];
function later(delay:number) {
return new Promise<void>(function(resolve) {
setTimeout(() => {
console.log('test');
resolve();
}, delay);
});
}
I only want it to execute when I call
await Promise.all(promises);
CodePudding user response:
Promises are values and not actions. Once you have a promise for something the corresponding action has already happened.
Luckily the language provides a neat abstraction for actions - functions. The way you typically make a promise "lazy" is by working with a function returning a promise rather than the promise itself.
(Note, this is unlike for example observables which are actions and subscribing to them invokes them)
CodePudding user response:
As other posts/comments said, later(1000)
is evaluated right away and don't wait for your await Promise.all
call to run the promise code.
you could achieve that by doing the following:
const promises = [() => later(1000)];
function later(delay:number) {
return new Promise<void>(function(resolve) {
setTimeout(() => {
console.log('test');
resolve();
}, delay);
});
}
await Promise.all(promises.map(f => f()));
CodePudding user response:
Wrap promises
in an anonymous function to defer execution. TS Playground
// defer promises
const promises = () => [later(1000)];
// unmodified
function later(delay:number) {
return new Promise<void>(function(resolve) {
setTimeout(() => {
console.log('test');
resolve();
}, delay);
});
}
// call the promises when ready
(async function () {
await Promise.all(promises());
})()
CodePudding user response:
And then there are thenables; in Typescript PromiseLike<T>
.
A naive implementation:
function later(delay) {
return {
then(onResolve, onReject) {
return new Promise(resolve => {
console.log("called new Promise()");
setTimeout(resolve, delay);
}).then(onResolve, onReject);
}
}
}
but for this use case we can completely get rid of the Promise:
function later(delay) {
return {
then(onResolve) {
console.log("called setTimeout()");
setTimeout(onResolve, delay);
}
}
}
This implementation assumes that you at most call later(n).then(...)
, like await
does. But not later(n).then(...).then(...)
or catch()
or finally()
or all that jazz. If you want all that, use the implementation that returns a Promise
function later(delay) {
return {
then(onResolve) {
console.log("called setTimeout()");
setTimeout(onResolve, delay);
}
}
}
const action = later(1000);
console.log("obj created");
// wait a bit so that its clear that new Promise ain't called immediately
setTimeout(async() => {
console.log("before awaiting");
await action;
console.log("after awaiting");
await action;
console.log("awaiting the same object a second time");
await action;
console.log("and a third time");
await later(3000); // also works
console.log("done");
}, 2000);
.as-console-wrapper{top:0;max-height:100%!important;}
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>