javascript - JS: async function implicitly unwraps promise? - Stack Overflow

To the best of my knowledge async functions wrap their returned value into a promise implicitly. This d

To the best of my knowledge async functions wrap their returned value into a promise implicitly. This does work for every property, except Promises themselfs.

async function f() {
  return new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })
}

(async () => {
  console.log(await f())
})()

Those get unwrapped before being returned. So that await f() actually awaits two nested promises.

Note that this also works with explicitly createded Promises (Promise.resolve(new Promise(...)))

Is there a good way to avoid this? I would really like to have a nested Promise without a quickfix like so.

async function y() {
  return {wrapped: new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })}
}

(async () => {
  console.log((await y()).wrapped)
})()

Sandbox

To the best of my knowledge async functions wrap their returned value into a promise implicitly. This does work for every property, except Promises themselfs.

async function f() {
  return new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })
}

(async () => {
  console.log(await f())
})()

Those get unwrapped before being returned. So that await f() actually awaits two nested promises.

Note that this also works with explicitly createded Promises (Promise.resolve(new Promise(...)))

Is there a good way to avoid this? I would really like to have a nested Promise without a quickfix like so.

async function y() {
  return {wrapped: new Promise((res) => {
    setTimeout(() => {
      res("Why am I being unwrapped")
    }, 1000)
  })}
}

(async () => {
  console.log((await y()).wrapped)
})()

Sandbox

Share Improve this question edited May 12, 2020 at 21:39 tadman 212k23 gold badges236 silver badges265 bronze badges asked May 12, 2020 at 21:32 MaximilianMairingerMaximilianMairinger 2,4433 gold badges17 silver badges39 bronze badges 7
  • Do you want an async function that returns a Promise when you call await on it? It's not clear what the confusion is. – tadman Commented May 12, 2020 at 21:34
  • 1 developer.mozilla/en-US/docs/Web/JavaScript/Reference/… - note this section: If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.. So if the return value is already a promise, it isn't wrapped. – Robin Zigmond Commented May 12, 2020 at 21:36
  • 4 If you want a promise to resolve to a promise, then you HAVE to wrap the inner one in an object so the first promise resolves to an object that contains a promise. That's true whether it's Promise.resolve(somePromise), returning a promise from a .then() handler or returning a promise in an async function. It's how it is designed. – jfriend00 Commented May 12, 2020 at 21:38
  • 1 Of interest is, why? It is designed the way it is because you pretty much always want promises to chain and for the parent promise to wait for child promises. What is your use case for not having the parent and child promises chained together? – jfriend00 Commented May 12, 2020 at 21:39
  • No. Flattening works on every thenable (object with a .then method). – Jonas Wilms Commented May 12, 2020 at 21:41
 |  Show 2 more ments

2 Answers 2

Reset to default 7

As per the MDN documentation on async functions:

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.

In other words, since you're returning a Promise it does not get wrapped. If you want to re-wrap it you could, but you could also strip the async keyword which has a similar effect. You get the raw Promise back unless you await it.

So effectively:

function f() {
  return new Promise((res) => {
    setTimeout(() => {
      res("Might be wrappers on some of you, but not on me!")
    }, 1000)
  })
}

(async () => {
  console.log(f())
})()

Which gives you output like:

Promise { <pending> }

If the goal is to have a Promise that returns a raw Promise then you're going to be fighting against everything that the Promise system is designed to do, namely automatically unwrap Promises as they're discovered. Promise.resolve() wraps everything but Promises, and if you give it a Promise somehow, it goes about unwrapping that recursively.

You can do this by returning something that is not a Promise, like in your example, but that does seem to defeat the entire purpose of Promises.

If you have a particular problem you're trying to solve that absolutely necessitates a deferred Promise, consider returning a function instead of some arbitrary object:

function f() {
  return () => {
    return new Promise((res) => {
      setTimeout(() => {
        res("I'll start as soon as you ask.")
      }, 1000)
    })
  };
}

Then you can call let p = f() and later p() when you want to get an actual Promise. This is how JavaScript normally handles deferred execution.

If you want to initiate the execution on the Promise immediately, you can still acmodate that:

function f() {
  const p = new Promise((res) => {
    setTimeout(() => {
      res("Already underway when requested.")
    }, 1000)
  })

  return () => { p };
}

Though these sorts of techniques are usually best avoided if at all possible, and usually they are.

It seems like you want a Future instead of a Promise. You can nest Futures as you wish, and they do not unwrap automatically. This page explains the difference between Futures and Promises: https://en.wikipedia/wiki/Futures_and_promises

Here is one way to make a Future in TS...

export class Future<T> {
    static Pending = 0;
    static Resolved = 1;
    static Rejected = 2;
    state = Future.Pending;
    err;
    val: T;

    constructor(readonly p: Promise<T>) {
        p.then(val => {
            this.val = val;
            this.state = Future.Resolved;
        });
        p.catch(err => {
            this.err = err || new Error();
            this.state = Future.Rejected;
        });
    }

    async get(): Promise<T> {
        return this.p;
    }

    isDone(): boolean {
        return this.state != Future.Pending;
    }
}

For JS, please just remove the type descriptors.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745428739a4627298.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信