Home > Mobile >  Why does this promise resolve when I am not awaiting it
Why does this promise resolve when I am not awaiting it

Time:11-11

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>

  • Related