使用异步与返回新 Promise 的区别。

huangapple go评论76阅读模式
英文:

difference using async vs return new Promise

问题

File 1: api/hello.js

export default function handler(req, res) {
  getCountries().then(result => {
    console.log('result = ', result);
    res.status(200).json({ status: 'ok', data: result.data });
  }).catch(err => {
    console.log('error = ', err);
    res.status(500).json({ status: 'error', error: err });    
  })
}

File 2: utils/getCountries.js

const getCountries = async () => {
    var response = [];
    var params = { action: "get_countries", ...authKey };
    try {
        const result = await axios.get(APIFOOTBALL_URL, { params: params });
        response = [...result.data];
        return response;
    } catch (err) {
        throw err;
    }
}

export default getCountries;

Changing the getCountries function to the following works, but I don't understand why.
Isn't async returning a promise as well?
If I have to write it in the async/await method, how should I go about?

File 2: utils/getCountries.js

const getCountries = () => {
    return new Promise((resolve, reject) => {
        var response = [];
        var params = { action: "get_countries", ...authKey };

        axios.get(APIFOOTBALL_URL, { params: params })
            .then((result) => {
                response = [...result.data];
                resolve(response);
            }).catch((err) => {
                reject(err);
            });
    })
}
英文:

I'm trying to call an async utility function from NextJS API page, but gets undefined.
The console prints the following when I go to localhost:300/api/hello

result =  undefined

File 1: api/hello.js

export default function handler(req, res) {
  getCountries().then(result=>{
    console.log('result = ', result);
    res.status(200).json({ status: 'ok', data: result.data });
  }).catch(err=>{
    console.log('error = ', err);
    res.status(500).json({ status: 'error', error: err });    
  })
}

File 2: utils/getCountries.js

const getCountries = async () => {
    var response = [];
    var params = { action: "get_countries", ...authKey }
    axios.get(APIFOOTBALL_URL, { params: params })
        .then((result) => {
          response = [...result.data];
          return response;
        }).catch((err) => {
            throw err;
        });
}

export default getCountries;

Changing the getCountries function to the following works, but I don't understand why.
Isn't async returning a promise as well?
If I have to write it in the async/await method, how should I go about?

File 2: utils/getCountries.js

const getCountries = () => {
    return new Promise((resolve, reject) =>{
        var response = [];
        var params = { action: "get_countries", ...authKey }
    
        axios.get(APIFOOTBALL_URL, { params: params })
            .then((result) => {
              response = [...result.data];
              resolve(response);
            }).catch((err) => {
                reject;
            });
    })
}

答案1

得分: 2

在你的第一个示例中,getCountries 函数返回一个空的 promise(undefined),而在第二个示例中,它返回了一个包含响应的 promise。

在第一个示例中发生的情况是,axios.get 函数运行,但没有任何东西真正阻止函数返回。调用 .thenaxios.get 上的整个目的是,无论 .then 回调中的内容在 promise 解析之后都会执行。否则,你可以像通常一样在 axios.get 调用之后写你要写在 .then 中的代码,如果你使用 await 的话,你可以这样做。使用 await 会阻止代码执行,直到 promise 被解析或拒绝(这就是为什么你应该只在 try-catch 块中使用 await 的原因)。

如果将你的第一个示例重构如下:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    try {
        const result = await axios.get(APIFOOTBALL_URL, { params: params });
        const response = [...result.data];
        return response;
    } catch (err) {
        console.error("An error occurred while trying to fetch countries: ", err);
        throw err;
    }
}

你最终会阻止 axios.get 调用之后的代码执行,直到该 promise 被解析或拒绝。

简而言之,你的第一个示例不起作用的原因是因为你的函数返回 undefined(隐式返回,因为在第一个示例中没有返回语句),在 axios.get 调用之后没有任何东西阻止它返回,因此你的第一个示例返回的 promise 总是以 undefined 值解析。

你的第二个示例之所以有效,是因为当你手动返回一个 promise 时,它始终保持挂起状态,直到你在代码中手动使用 resolve(response);reject 来解析它。

如果这不够清楚,可以这样考虑 -> 在调用代码中,当你执行 getCountries.then 时,.then 部分仅在 promise 被解析时才执行。它被解析的值传递给回调函数(你将其用作 result)。在你的第一个示例中,getCountries 立即解析为一个值 undefined,所以你的回调得到的是 undefined 值,而在第二个示例中,getCountries 返回的 promise 一直挂起,直到在你的代码中调用 resolve(因为你在 axios.get.then 回调中调用它)。

另外,在第一个示例中,当你在 axios.get.then 部分执行 return response; 时,你返回的是 .then 回调函数的返回值,而不是从 getCountries 函数返回。也许这让你认为这应该有效,但实际上不起作用。

你的第一个示例也可以通过这样做来使其工作:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    return axios.get(APIFOOTBALL_URL, { params: params })
        .then((result) => {
          const response = [...result.data];
          return response;
        }).catch((err) => {
            throw err;
        });
}

因为现在,getCountries 返回的是包含响应的 promise,而不仅仅是返回一个返回 undefined 的 promise。

最后,正如 @jfriend00 提到的,没有理由在 .catch() 中只是再次抛出错误。catch 的目的是处理错误(记录它或处理它)。你现在使用 .catch() 的方式与不使用它完全一样。

英文:

In your first example of getCountries, you are returning a promise of nothing (undefined), whereas in the second example, you are returning a promise of the response.

What happens in your first example is, axios.get runs, but then nothing is really stopping the function from returning. The entire point of calling .then on axios.get is so that whatever is in the .then callback gets executed after the promise is resolved. Otherwise you could have just written the code you're writing in .then after the axios.get call as usual, which you CAN do, if you use await. Using await would stop the code execution until the promise is either resolved or rejected (which is why you should only use await in a try-catch block.

If your first example is refactored as follows:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    try {
        const result = await axios.get(APIFOOTBALL_URL, { params: params });
        const response = [...result.data];
        return response;
    } catch (err) {
        console.error("An error occurred while trying to fetch countries: ", err);
        throw err;
    }
}

you end up blocking the execution of the code after the axios.get call until that promise is resolved or rejected.

In short, the reason your first example doesn't work, is because your function returns undefined (implicitly as there's no return statement in your first example) after the axios.get call. Nothing stops it from returning, and so the promise your first example is returning is always resolved with undefined value.

The reason your second example DOES work, is because when you're manually returning a promise, it is always pending until you manually resolve it with resolve(response); or reject.

If that doesn't help, think about it this way -> In the calling code, when you do getCountries.then, The .then part only executes when the promise is resolved. The the value it is resolved with is passed to the callback (which you use as result). In your first example, getCountries immediately resolves with a value of undefined, so your callback gets the undefined value, whereas in the second example, the promise that getCountries returns is pending until resolve is called in your code, which only happens after your API request promise resolves (since you're calling it in the .then callback of axios.get).

Also, in your first example, when you do return response; in the .then part of axios.get, you are returning from the callback function of .then. This does not return from the getCountries function. Maybe that is causing you to think this should work, but doesn't.

Your first example can also be made to work if you do this:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    return axios.get(APIFOOTBALL_URL, { params: params })
        .then((result) => {
          const response = [...result.data];
          return response;
        }).catch((err) => {
            throw err;
        });
}

because now, getCountries is returning a promise of the response, instead of just returning a promise of undefined.

Lastly, as @jfriend00 mentioned, there is no reason for a .catch() that only throws the error again. The point of catch is to do something with the error (log it or handle it). The way you are using .catch() right now will have the same behaviour as not using it at all.

答案2

得分: 1

在一个async函数中,你必须要么await你的Promise,要么返回它。如果没有其中一个,实际上没有任何东西等待该Promise或者将其结果返回。因为使用async的目的是为了能够使用await,以下是示例:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    const result = await axios.get(APIFOOTBALL_URL, { params: params });
    return result.data;
}

或者,你可以只返回Promise,而不需要async

const getCountries = () => {
    const params = { action: "get_countries", ...authKey }
    return axios.get(APIFOOTBALL_URL, { params: params }).then(result => {
        return result.data;
    });
}

关于你的代码还有一些其他注意事项:

  1. 不再需要使用var。在现代JavaScript中,请使用letconst
  2. 不需要使用response = [...result.data];来复制数据。你可以直接使用return result.data
  3. 你的最后一个代码块被视为Promise反模式。不要将现有的Promise(从axios.get()调用返回的Promise)与手动创建的Promise包装起来。这不是必要的,通常会在错误处理方面出现问题,因为你没有正确地调用reject(err)
  4. 除非你在.catch()中还有其他代码(例如记录错误),否则没有理由只使用throw err

请注意,以上内容是代码部分的翻译,不包括问题部分。

英文:

In an async function, you must either await your promise or you must return it. Without one of those, nothing actually waits for that promise or communicates back any result from it. Since the point of using async is to be able to use await, here's what that would look like:

const getCountries = async () => {
    const params = { action: "get_countries", ...authKey }
    const result = await axios.get(APIFOOTBALL_URL, { params: params });
    return result.data;
}

Or alternately, you can just return the promise and don't need async:

const getCountries = () => {
    const params = { action: "get_countries", ...authKey }
    return axios.get(APIFOOTBALL_URL, { params: params }).then(result =>{
        return result.data;
    });
}

A couple other notes about your code:

  1. There is no reason to use var any more. Use let or const instead in all modern Javascript.
  2. There's no reason to copy the data with response = [...result.data];. You can just do return result.data.
  3. Your last code block is considered a promise anti-pattern. You never want to wrap an existing promise (the one return from the axios.get() call) with a manually created promise. It's just not necessary and often creates problems with error handling which indeed you have because you didn't call reject(err) properly.
  4. There's no reason for a .catch() that only does throw err unless you also have some other code in that .catch() (such as logging the error).

huangapple
  • 本文由 发表于 2023年1月8日 01:06:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75042226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定