0

find is a function that returns a promise that resolves to a value, but also has data on the promise object itself. A practical use case is when find() returns a dbQueryObject that can be used elsewhere, whereas await find() resolves with databaseResults. eg:

function find () {
  var results = new Promise(/* resolves with databaseResults */)
  Object.assign(results, new dbQueryClass)
  return results
}

I can wrap find with a function to provide helpers like this,

function findPage (page) {
 return find().skip(page*20).limit(20)
}

and use it like findPage().select('_id') to get the query object or await findPage().select('_id') to get the resolved value (returns something similar to find()).

However, if I wrap find with a promise like this,

async function findSomething() {
  var someArgs = await promise1()
  if (someArgs) return find(someArgs)
  return find()
}

How do I get the value of find() itself outside of findSomething? I need promise1 to resolve, but I need find() to not resolve. find() is a plain object AND a thenable, depending on if you resolve it or not, but resolving findSomething() will automatically resolve the returned value. How do I return it such that it doesn't resolve?

My constraint is that I cannot modify the library that provides find() to not return a thenable object.

az_
  • 1,493
  • 1
  • 16
  • 24
  • I don't understand how `Promise.resolve` can execute a query. If it is passed a thenable, then it is essentially a no-op. Anyway, what is your question exactly? –  Apr 01 '16 at 03:39
  • my question is, how do I not resolve this thenable but do resolve the thenables before this? – az_ Apr 04 '16 at 19:52

2 Answers2

1

I don't see what would "usually" be an antipattern here. You can return or await any promise, thenable or plain value in an async function, just as you can Promise.resolve them or return them from a then callback. There is nothing unusual about this, that's just how it is done.

If you want to be explicit about returning a promise, instead of relying on thenable assimilation, you can always call .exec() on your mongoose query and it will get you a "real" promise.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • No it won't give me a real promise, it'll give me the resolved value of the promise. – az_ Apr 04 '16 at 19:51
  • 1
    @aaronz: That's quite unlikely, as that would mean the query would be synchronous. Are you sure that it's not `await ….exec()` that gives you the fulfillment value? – Bergi Apr 04 '16 at 20:00
  • I've updated the question to make it more clear + more examples. If I `return mongooseQuery()` within a promise, then awaiting that outer promise will give me the fulfillment value of the mongooseQuery. I'd like to get the promise/object without it being `Promise.resolve()`ed. – az_ Apr 04 '16 at 21:17
  • The mongoose query is both sync and async. When called with await/Promise.resolve, it returns the fulfillment value. When called synchronously, it returns a Query object (which can be used elsewhere for chaining). – az_ Apr 04 '16 at 21:19
  • 1
    Ah, thanks for the edit, it's clear now what your problem is. Unfortunately, [it's impossible to do this](http://stackoverflow.com/q/32168194/1048572). The best thing you'll get is setting `.then` to `null` so that you don't have a thenable any more. But still, a promise for a Query object looks a bit weird to me, you'd have to call `await (await findSomething()).select('id')` or so. Is that really necessary, is it the optimal API? – Bergi Apr 04 '16 at 22:18
1

As stated here https://stackoverflow.com/a/22724984/704894, it's impossible to have Promise<Promise<value>> because when you return a Promise inside .then handler, it gets automatically resolved (unwrapped).

You'd have to use Promise<Wrapper<Promise>> to bypass this limitation.

Sidenote for functional programming folks: .then is both map and flatMap in Haskell terms.

Michał Miszczyszyn
  • 11,835
  • 2
  • 35
  • 53