“我调用了一个函数,但它返回 undefined!”——熟悉的心痛吗?
\n想象一下:你在编写 JavaScript 代码,向 API 发起请求,等待数据……但返回的却是 undefined。或者更糟——Promise {<pending>}。你垂头丧气,想关掉浏览器去当和尚。
别急。这个问题连经验丰富的开发者都会困扰。难怪 Stack Overflow 上的问题 “How do I return the response from an asynchronous call?” 获得了数千次浏览和数百个回答。今天我们就一劳永逸地解决它。
\n在本文中,你将了解到:
\n- \n
- 为什么
return不像你习惯的那样工作 \n - 如何通过回调、Promise 和
async/await驯服异步操作 \n - 为你的项目选择哪种方法 \n
开始吧!
\n\n为什么 return 不返回数据?
\n我们直接进入正题。这是一个经典示例:
\n\nfunction getUserData(userId) {\n fetch(`https://api.example.com/users/${userId}`)\n .then(response => response.json())\n .then(data => {\n return data; // 看起来没问题?\n });\n}\n\nconst user = getUserData(42);\nconsole.log(user); // undefined\n\n熟悉吗?你在 .then() 内部写了 return data,但在外部却得到了 undefined。为什么?
JavaScript 是单线程且非阻塞的。 当你调用 fetch 时,它不会等待服务器响应。相反,它将回调的执行“搁置”到一边,并立即转到下一行代码。而下一行是 console.log(user),此时 user 尚未定义。
你的 getUserData 函数在服务器响应返回之前就结束了。它没有返回任何内容(返回 undefined),而数据则“悬在空中”。
如何解决?有三种主要方法。
\n\n方法 1:回调(Callbacks)——异步的鼻祖
\n在过去,Promise 出现之前,所有异步操作都通过回调完成。你传递一个函数,当数据到达时该函数会被调用。
\n\nfunction getUserData(userId, callback) {\n fetch(`https://api.example.com/users/${userId}`)\n .then(response => response.json())\n .then(data => {\n callback(data); // 用数据调用回调\n })\n .catch(error => {\n callback(null, error); // 或者传递错误\n });\n}\n\n// 使用方式:\ngetUserData(42, (data, error) => {\n if (error) {\n console.error('错误:', error);\n return;\n }\n console.log('用户数据:', data);\n});\n\n优点: 简单,随处可用。
\n缺点: 如果嵌套多个回调,就会形成“回调地狱”(callback hell)。代码变得难以阅读。
\n\ngetUserData(42, (data1) => {\n getPosts(data1.id, (data2) => {\n getComments(data2[0].id, (data3) => {\n // ... 以此类推\n });\n });\n});\n\n现代 JavaScript 已经摆脱了这种写法。但理解回调仍然必要——它们是所有其他方法的基础。
\n\n方法 2:Promise(承诺)——可读性的救星
\nPromise 是一个对象,它代表一个“承诺”在未来返回某个值。它有三种状态:pending(等待中)、fulfilled(已完成)和 rejected(已拒绝)。
回到我们的示例。我们不使用回调,而是直接返回 Promise 本身:
\n\nfunction getUserData(userId) {\n return fetch(`https://api.example.com/users/${userId}`)\n .then(response => {\n if (!response.ok) {\n throw new Error('网络异常');\n }\n return response.json();\n });\n}\n\n// 使用方式:\nconst userPromise = getUserData(42);\n\nuserPromise\n .then(data => {\n console.log('用户数据:', data);\n })\n