3

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.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
BaReinhard
  • 33
  • 4
  • You can't `await` `setTimeout` (it doesn't return a `Promise`), you would need to create a delay function that returns a Promise for that to work: `const delay = duration => new Promise(resolve => setTimeout(resolve, duration));` then await that `await delay(() => ..., random)` – Rob M. Sep 08 '17 at 16:54
  • Thanks for that catch, I removed it from the OP. But now that you mention it, if an async function runs through all its code synchronously and doesn't have a return or throw, will it automatically return undefined, or will it (in this case) wait for the settimeout and return the value? – BaReinhard Sep 08 '17 at 16:59
  • Not sure if I'm understanding your question, but if you are asking what is returned if you run an async function without `await`: it will return a Promise. – Rob M. Sep 08 '17 at 17:09
  • Sorry, let me try to clarify. For explanation sake say in the async function we have no code inside the async function, does the async function go through the code, sees there is no return statement will the async function assume we forgot to place a return statement then resolve and return some default value? or will the return value of this async function be a pending promise indefinitely? – BaReinhard Sep 08 '17 at 17:23
  • 2
    I think I understand now, `async` is just syntactic sugar over Promises - so an empty async function creates a Promise for you and resolves to the return value, if you don't return anything, then you get a promise that resolves `undefined` (https://jsfiddle.net/7z960211/). – Rob M. Sep 08 '17 at 17:56
  • Ah yes that was what I was asking, it didn't occur to me that that might have been the case until you reminded me that set timeout doesn't return a promise and needs a promise wrapper. Thank you for the clarification. – BaReinhard Sep 08 '17 at 18:30

1 Answers1

2
 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...)

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151