0

I am working on a GraphQL query where I am trying to find a unique model. However, nothing ever gets returned because the code kept carrying on before the query was finished, thus attempted to return a Promise when it expected a Model. The code looks as follows...

          const findShift = async (date) => {
                console.log("In mutation function")
                const foundShift = await db.shift.findUnique({
                    where: {
                        date: date
                    }
                })
                return foundShift
            }
    
            const foundShift = findShift(date).then( resolved => {
                console.log("printing resolved...")
                console.log(resolved)
                if (resolved.id != 'undefined'){
                    console.log({
                        id: resolved.id,
                        date: resolved.date,
                        allDevices: resolved.allDevices
                    })
                    return foundShift
                }
                else{
                    throw new Error("no shift of that date found!")
                }
            })

And the console.log statements make the console look as so...

In mutation function
Promise { <pending> }
prisma:info Starting a postgresql pool with 9 connections.

and ultimately the query just returns null. As you see, I tried using then and putting the mutation itself into an entirely different function just to circumvent these asynchronisity issues to no avail. Does anyone see a workaround?

  • As you can see in the question yours is marked a duplicate of, ALL `async` functions return a promise. The return value in the `async` function becomes the resolved value of that promise. So, the caller of an `async` function MUST use `.then()` or `await` to get the resolved value from the `async` function. There is no way to "circumvent" the asynchronicity like you are attempting. You can tame it to make it more usable, but you can't escape it. So, your `async` function returns a pending promise that will eventually resolve to whatever value you `return` inside your `async` function. – jfriend00 Apr 06 '22 at 15:21
  • How then would I get this query to return something other than null? It cannot even make it passed the line `f (resolved.id != 'undefined'){` since `.id` doesn't have a value. I thought using then `then()` after the async function would help this work. What can I do to make this query to work as anticipated? @jfriend00 –  Apr 06 '22 at 15:36
  • What does your `console.log(resolved)` show? It doesn't look to me like your `findShift()` function would even work properly because `foundShift` is defined inside the `try/catch`, but you try to return it from outside the `try/catch`. – jfriend00 Apr 06 '22 at 16:27
  • That's the line that only prints `Promise { }` –  Apr 06 '22 at 18:04
  • I don't think so - not from the code you show in the question right now. `findShift(date).then( resolved => { console.log(resolved)})` isn't going to show a promise. This question is very confused. As I said above, the code you show for `findShift()` won't even run properly. We can't help you any further if you're going to leave the code in your question this way. – jfriend00 Apr 06 '22 at 18:08
  • I added the try and catch as a late measure. Removing it provides the exact same output and the exact same null return. And yes I'm positive its that line. This question is not confused. –  Apr 06 '22 at 18:35

1 Answers1

0

First off, ALL async functions return a promise. The return value in the async function becomes the resolved value of that promise. So, the caller of an async function MUST use .then() or await to get the resolved value from the async function. There is no way to "circumvent" the asynchronicity like you are attempting. You can tame it to make it more usable, but you can't escape it. So, your async function returns a pending promise that will eventually resolve to whatever value you return inside your async function.

You can read more about how async functions work here in this other answer.


In trying to make a minimal, reproducible example of your code, I've reduced it to this where I've substituted an asynchronous simulation for the database call:

function delay(t, v) {
    return new Promise(resolve => setTimeout(resolve, t, v));
}

// simulate asynchronous database operation
const db = {
    shift: {
        findUnique: function(data) {
            return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
        }
    }
}

const findShift = async (date) => {
    console.log("In mutation function")
    const found = await db.shift.findUnique({
        where: {
            date: date
        }
    })
    return found;
}

const date = Date.now();

const foundShift = findShift(date).then(resolved => {
    console.log("printing resolved...")
    console.log(resolved);
    if (resolved.id != 'undefined') {
        console.log({
            id: resolved.id,
            date: resolved.date,
            allDevices: resolved.allDevices
        })
        return foundShift
    } else {
        throw new Error("no shift of that date found!")
    }
});

When I run this in nodejs, I get this error:

[TypeError: Chaining cycle detected for promise #<Promise>]

And, the error is caused by this line of code:

return foundShift

You are attempting to return a promise that's already part of this promise chain from within the promise chain. That creates a circular dependency which is not allowed.

What you need to return there is whatever you want the resolved value of the parent promise to be. Since that looks like it's the object you construct right above it, I've modified the code to do that. This code can be run and foundShift is a promise that resolves to your object.

function delay(t, v) {
    return new Promise(resolve => setTimeout(resolve, t, v));
}

// simulate asynchronous database operation
const db = {
    shift: {
        findUnique: function(data) {
            return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
        }
    }
}

const findShift = async (date) => {
    const found = await db.shift.findUnique({
        where: {
            date: date
        }
    })
    return found;
}

const date = Date.now();

const foundShift = findShift(date).then(resolved => {
    if (resolved.id != 'undefined') {
        let result = {
            id: resolved.id,
            date: resolved.date,
            allDevices: resolved.allDevices
        };
        return result;
    } else {
        throw new Error("no shift of that date found!")
    }
});

// foundShift here is a promise
// to get it's value, you have to use .then() or await on it
foundShift.then(result => {
    console.log("final result", result);
}).catch(e => {
    console.log(e);
});

Here are a couple of rule about promises that might help:

  1. All fn().then() or fn().catch() calls return a new promise that is chained to the one that fn() returned.
  2. All async functions return a promise.
  3. You cannot "circumvent" asynchronicity and somehow directly return an asynchronously retrieved value. You will have to use a callback, an event or return a promise (or some similar asynchronous mechanism) in order to communicate back to the caller an asynchronously retrieved value.
  4. await can only be used inside an async function (or at the top level of an ESM module).
  5. The first await in a function suspends execution of the async function and then immediately returns an unfulfilled promise to the caller. So, the await only affects the current function flow, not the caller's flow. The caller will still have to use .then() or await to get the value out of the promise that the async function returns.
  6. Try as you might, there is no way around these rules (in Javascript as it currently runs in a browser or in nodejs).
jfriend00
  • 683,504
  • 96
  • 985
  • 979