Search code examples
javascriptasync-awaitbabeljscreate-react-appecmascript-2017

Imported async functions don't properly resolve data


Background:

I have been using create-react-app to create React components and my latest project requires a server backend to return data.

I like to mock up the data returned by the API with imported functions from a 'API' file.

Recently, I have begun to adopt the newer async/await functions, mainly because they are easier to read through.

Issue:

In my one of my components I have imported these API functions, I originally created them as async functions (which to my understanding, by default, return a promise and will resolve the values via the return keyword and reject via the throw keyword).

However, when I debug the code I see that it calls the async function, then immediately continues to console the "result" which is undefined, this also will happen if I use a .then(res=>{console.log(res)}); it immediately enters the then callback function without waiting for the promise to resolve.

**The code used to call these functions: **

// I have removed all the other lines of code and left the important code
import { getVideoList } from './api';
runMe = async () => {
    let res = await getVideolist();
    console.log(res);
}
<button onClick={this.runMe}>Click Me</button>

The thing is that when wrapping the contents of the functions with Promises and using the Promises resolve function, it works properly.

Here is the original code:

export let getVideoList = async () => {
    let random = Math.floor(Math.random() * 3000);
    setTimeout(() => {
        let res = [
            {
                video_ID: 3,
                video_url: 'https:google.com/3',
                video_description: 'A basic video of exercise',
                tags: ['upper-body', 'biceps', 'triceps'],
            },
            {
                video_ID: 2,
                video_url: 'https:google.com/2',
                video_description: 'A basic video of exercise',
                tags: ['upper-body', 'biceps', 'triceps'],
            },
            {
                video_ID: 1,
                video_url: 'https:google.com/1',
                video_description: 'A basic video of exercise',
                tags: ['upper-body', 'biceps', 'triceps'],
            },
        ];
        return res;
    }, random);
};
export let getTags = async () => {
    let random = Math.floor(Math.random() * 3000);
    setTimeout(() => {
        return [
            { tag_ID: 1, tagName: 'upper-body' },
            { tag_ID: 2, tagName: 'biceps' },
            { tag_ID: 3, tagName: 'triceps' },
            { tag_ID: 4, tagName: 'shoulders' },
        ];
    }, random);
};
export let getVideos = async () => {
    let random = Math.floor(Math.random() * 3000);
    setTimeout(() => {
        let res = [
            { video_ID: 3, video_url: 'https:google.com/3', video_description: 'A basic video of exercise' },
            { video_ID: 2, video_url: 'https:google.com/2', video_description: 'A basic video of exercise' },
            { video_ID: 1, video_url: 'https:google.com/1', video_description: 'A basic video of exercise' },
        ];
        return res;
    }, random);
};

Here is the revised code that works:

export let getVideoList = async () => {
    return new Promise((res, rej) => {
        let random = Math.floor(Math.random() * 3000);
        setTimeout(() => {
            res([
                {
                    video_ID: 3,
                    video_url: 'https:google.com/3',
                    video_description: 'A basic video of exercise',
                    tags: ['upper-body', 'biceps', 'triceps'],
                },
                {
                    video_ID: 2,
                    video_url: 'https:google.com/2',
                    video_description: 'A basic video of exercise',
                    tags: ['upper-body', 'biceps', 'triceps'],
                },
                {
                    video_ID: 1,
                    video_url: 'https:google.com/1',
                    video_description: 'A basic video of exercise',
                    tags: ['upper-body', 'biceps', 'triceps'],
                },
            ]);
            return res;
        }, random);
    });
};
export let getTags = async () => {
    return new Promise((res, rej) => {
        let random = Math.floor(Math.random() * 3000);
        setTimeout(() => {
            res([
                { tag_ID: 1, tagName: 'upper-body' },
                { tag_ID: 2, tagName: 'biceps' },
                { tag_ID: 3, tagName: 'triceps' },
                { tag_ID: 4, tagName: 'shoulders' },
            ]);
        }, random);
    });
};
export let getVideos = async () => {
    return new Promise((res, rej) => {
        let random = Math.floor(Math.random() * 3000);
        setTimeout(() => {
            res([
                { video_ID: 3, video_url: 'https:google.com/3', video_description: 'A basic video of exercise' },
                { video_ID: 2, video_url: 'https:google.com/2', video_description: 'A basic video of exercise' },
                { video_ID: 1, video_url: 'https:google.com/1', video_description: 'A basic video of exercise' },
            ]);
        }, random);
    });
};

I am unsure as to why this is happening, I have tried searching and have only come up with new topics of using the import asynchronously.

While this is not much of an issue here with this project I would like to get to the bottom of this for future projects.

Revised code to work with async/await:

const timer = ms => new Promise(res => setTimeout(res, ms));
export let getVideoList = async () => {
    let random = Math.floor(Math.random() * 3000);
    await timer(random);
    let res = [
        {
            video_ID: 3,
            video_url: 'https:google.com/3',
            video_description: 'A basic video of exercise',
            tags: ['upper-body', 'biceps', 'triceps'],
        },
        {
            video_ID: 2,
            video_url: 'https:google.com/2',
            video_description: 'A basic video of exercise',
            tags: ['upper-body', 'biceps', 'triceps'],
        },
        {
            video_ID: 1,
            video_url: 'https:google.com/1',
            video_description: 'A basic video of exercise',
            tags: ['upper-body', 'biceps', 'triceps'],
        },
    ];
    return res;
};
export let getTags = async () => {
    let random = Math.floor(Math.random() * 3000);
    await timer(random);
    return [
        { tag_ID: 1, tagName: 'upper-body' },
        { tag_ID: 2, tagName: 'biceps' },
        { tag_ID: 3, tagName: 'triceps' },
        { tag_ID: 4, tagName: 'shoulders' },
    ];
};
export let getVideos = async () => {
    let random = Math.floor(Math.random() * 3000);
    await timer(random);
    let res = [
        { video_ID: 3, video_url: 'https:google.com/3', video_description: 'A basic video of exercise' },
        { video_ID: 2, video_url: 'https:google.com/2', video_description: 'A basic video of exercise' },
        { video_ID: 1, video_url: 'https:google.com/1', video_description: 'A basic video of exercise' },
    ];
    return res;
};

FIX:

The issue stems from trying to return a value inside setTimeout.


Solution

  •  await setTimeout(_=>{...},random)
    

    wont work as setTimeout doesnt return a promise. May promisify it:

     const timer = ms => new Promise( res => setTimeout(res,ms));
    

    So you can do

    async whatever(){
      await timer(1000);
      return { ... };
    }
    

    ( Small tip: returning from inside a timeout does nothing...)