Home > Blockchain >  Promise Stays Pending, Returns Null
Promise Stays Pending, Returns Null

Time:04-12

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?

CodePudding user response:

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).
  • Related