Daniel Thompson
Daniel Thompson

Reputation: 2351

Async function in a promise callback

I am trying to do something like this but it's breaking:

myAsyncFunction = async () => {
    fetchResource().then( res => {
        await fetchAnotherResource()
        ....
    })
}

Within the completion of the first promise, I want to use await expressions, but it doesn't look like the scope of that callback allows for async functions.

With the way it is above, it tells me that await is a reserved keyword (it's not picking up any meaning for await in this case, because of scope I'm guessing?)

If I declare the anonymous function with async like this:

fetchResource().then( async (res) => {...})

I get a syntax error like this:

ProductForm.jsx?130a:143 Uncaught (in promise) TypeError: (0 , _context.t1) is not a function

I'm not sure if it's relevant, but this is inside of a React component.

Is there a syntax for accomplishing what I'm trying to do here? (Use an async function as a promise handler)

Edit: relevant code (sorry for code dump)

initializeState = async () => {
    getShopInfo(this.state.shop_id)
    .then (async (res) => {
        if (product_id) {
            ({ activeLocalization, localizations } = await this.initializeEditState(product_id, defaultLocalization, localizations))
        } else {
            ({ activeLocalization, localizations } = await this.initializeBlankState(defaultLocalization, localizations))
        }
    })
}

initializeEditState = (product_id, defaultLocalization, localizations, allCategoriesPromises) => {
    return new Promise( resolve => {
        let productPromiseArray = []; // storage for auxiliary localization requests
        let filledOutLocalizations = []; // hold localizations that have been merged with the product data 
        getProduct(product_id, defaultLocalization.localization_id)
        .then( json => {
            /* fetch object that will represent a product in state 
            * and merge it with the default localization object */
            let defaultProductState = this.setupEditProductState(json)
            defaultLocalization = Object.assign(defaultLocalization, defaultProductState)

            /* setup promise array for all other localizations */
            localizations.forEach( l => {
                productPromiseArray.push(getProduct(product_id, l.localization_id)) 
            })

            /* fetch other products and merge them with localizations */
            Promise.all( productPromiseArray )
            .then( resultArray => {
                resultArray.forEach( res => {
                    /* test for fail condition here;
                    * fail condition happens when new localizations 
                    * are registered between edits */
                    let loc = localizations.find( 
                        l => l.localization_id ==  res.data.product.localization_id
                    )
                    if (res.result) {
                        loc = Object.assign(loc, this.setupEditProductState(res))
                    } else {
                        /* Default state for this localization 
                        * (you should have access to defaultLocalization) */
                        loc = Object.assign(defaultProductState, loc)
                    }
                    filledOutLocalizations.push(loc)
                }) 
                /* Finally, get ALL category lists and
                    * INITIALIZE EDIT STATE */
                let promiseArray = []
                for (var filLoc of filledOutLocalizations) {
                    promiseArray.push(getAllCategories(filLoc.localization_id))
                }

                resolve({ activeLocalization: defaultLocalization, localizations: filledOutLocalizations })
            })
        })
    })
}

initializeBlankState = ( defaultLocalization, localizations ) => {
    return new Promise( resolve => {
        let activeLocalization = Object.assign( 
            defaultLocalization, 
            this.getBlankState(defaultLocalization.localization_id)
        )
        activeLocalization.product.enable_flg = 1
        localizations = localizations.map( l => {
            return Object.assign(l, this.getBlankState(l.localization_id))
        })
        resolve({ activeLocalization, localizations })
    })
}

Upvotes: 0

Views: 425

Answers (2)

blaz
blaz

Reputation: 4068

The code does not work in your original version because await can only be used inside an async function. The function inside then has its own scope, which does not relate to the outer one anymore; thus using await there is invalid.

The second version, in which you put async inside then is a violation of return type. From MDN definition of Promise.prototype.then:

The then() method returns a Promise

and MDN definition of async function:

The async function declaration defines an asynchronous function, which returns an AsyncFunction object

you can see that those two should not be used together.

Normally, async-await is used to replace Promise - then - catch, not for using together. So for your sample function, it should be:

myAsyncFunction = async () => {
    const resource = await fetchResource();
    if (isValidResource(resource)) {
        const anotherResource = await fetchAnotherResource();
        ...
    }
}

And for your function initializeState, which uses async:

initializeState = async () => {
    const res = await getShopInfo(this.state.shop_id);
    if (product_id) {
        ({ activeLocalization, localizations } = await this.initializeEditState(product_id, defaultLocalization, localizations))
    } else {
        ({ activeLocalization, localizations } = await this.initializeBlankState(defaultLocalization, localizations))
    }
}

Upvotes: 2

Vasyl Moskalov
Vasyl Moskalov

Reputation: 4630

From my point of view async inside then(..) is redundant in your case. Just continue use Promises.In fetchResource().then( res => fetchAnotherResource()).then(res=>{code...}) last code will wait completion of fetchAnotherResource(..). So, your code

.then (async (res) => {
    if (product_id) {
        ({ activeLocalization, localizations } = await this.initializeEditState(product_id, defaultLocalization, localizations))
    } else {
        ({ activeLocalization, localizations } = await this.initializeBlankState(defaultLocalization, localizations))
    }
})

can be rewrite like

.then ((res) => product_id?
                 this.initializeEditState(product_id, defaultLocalization, localizations)):
                 this.initializeBlankState(defaultLocalization, localizations))
)
.then(({activeLocalization,localizations})=>{.....})

Upvotes: 1

Related Questions