简体   繁体   中英

How to construct a Promise that calls an async function inside of it, parses the result, and then resolves/rejects?

I would like to write a function getUser() that returns a Promise where upon resolve it delivers the user object and upon reject it delivers the error. I'm using axios to make the request.

Here's what I currently have.

async function getUser() {

    try {
        const userResponse = await axios({
            method: 'post',
            url: "some_url",
            data: "some string data",
            headers: {
                'content-type': 'application/x-www-form-urlencoded'
            }
        });
        if (userResponse.data.status === 'success') {
            // resolve(validateResponse.data);
        } else {
            // reject(validateResponse.data);
        }
    } catch (error) {
        // reject(validateResponse.data);
    }

}

I would like to wrap the entire try...catch block inside a Promise constructor, but the Promise constructor is new Promise((resolve, reject) => {...}) and not new Promise(async (resolve, reject) => {...}) . I know I can do the latter but it's an anti-pattern .

The Promise approach doesn't necessarily work either because when I use the Promise returned by getUser() and .catch() any errors, I won't know if the error came from the response from the server (the status was not 'success' ) or if the request itself failed.

What is the safest way to get around this problem? I know there is a brute force solution of just mimicking the Promise API without actually using it, or declaring an async Promise, but I feel like I'm encountering an anti-pattern every time.

Thanks for any suggestions.

I'll answer this conceptually.

When you call an async function, it immediately returns a promise to the code-scope that called that function.

Inside of an async function, you don't need to use resolve and reject . Instead you can just use return and throw . What ever you return is what the promise will resolve to. If you throw , that's what the promise will "reject" to. Try something like this:

async function getUser() {

    try {
        const userResponse = await axios({
            method: 'post',
            url: "some_url",
            data: "some string data",
            headers: {
                'content-type': 'application/x-www-form-urlencoded'
            }
        });
        if (userResponse.data.status === 'success') {
            return validateResponse.data;
        } else {
            throw new Error(validateResponse.data);
        }
    } catch (error) {
        throw error;
    }
}

After async/await came out, I stopped liking the then syntax when dealing with promises. To avoid then , call getUser from the body of an async function. If you're not calling getUser from the body of an async function, you won't be able to await what it resolves to; you'll have to use then instead. Or, you can wrap that calling code in an immediately invoked async function just so you can indeed uses await . Here's a rough example of how my code (that calls getUser ) might look:

(async function()
{
    let user = await getUser();
    if(!user)
    {
        console.error(`Access Denied`);
        return;
    }
    // Rest of your code that should only run when you have a user goes here.
})();

The only reason I'm wrapping the code above in an immediately invoked async function, is so I can use await instead of then to resolve that promise.

Functions marked with async are meant to be written using promise .then() callback style. That is, in order to resolve a value you use the return keyword and in order to reject a value you use the throw keyword. Thus your function should be written as follows:

async function getUser() {

    try {
        const userResponse = await axios({
            method: 'post',
            url: "some_url",
            data: "some string data",
            headers: {
                'content-type': 'application/x-www-form-urlencoded'
            }
        });
        if (userResponse.data.status === 'success') {
            return validateResponse.data;
        } else {
            throw new Error(validateResponse.data);
            // Alternately you can also just:
            // throw validateResponse.data
        }
    } catch (error) {
        throw new Error(validateResponse.data);
    }

}

The Promise approach doesn't necessarily work either because when I use the Promise returned by getUser() and .catch() any errors, I won't know if the error came from the response from the server (the status was not 'success') or if the request itself failed.

I would look at rejecting the outer promise inside getUser in one of two examinable ways - and then call getUser using a promise topology that splits good data into a processing stream (chain of promises), and errors into another for differentiation. For example:

  1. Create a data wrapper constructor called, say, GetUserData:

     const GetUserData = function( data) { this.data = data; }

    This may benefit from having another parameter for getUser argument values.

  2. Remove the try { and catch() {} clauses. Let axios errors throw and reject the getUser promise.

  3. Have getUser return the result of "success" or throw a GetUserData object:

     if (userResponse.data.status === 'success') { return userResponse.data; } else { throw new GetUser( userResponse.data); }
  4. Call getUser with a topology that differentiates the error types:

     let userPromise = getUser(); // parameterized? userPromise.then(data => { do something with user data}); // chain promises on "success" as needed // split errors into a .then handler for server errors and a .catch handler for axios errors: userPromise.catch( something => { if( something instanceOf GetUserData) { return something.data; } throw something; // rethrow axios error }) .then ( data => { deal with unsuccessful server data}) .catch( error => { deal with axios error })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM