JavaScript async函数的返回值处理
2021-10-133.4k 阅读
JavaScript async 函数的返回值处理
一、async 函数基础回顾
在深入探讨 async 函数返回值处理之前,我们先来简要回顾一下 async 函数的基本概念。在 JavaScript 中,async
函数是一种异步函数,它返回一个 Promise
对象。当 async
函数执行时,如果其返回值不是一个 Promise
,JavaScript 会自动将其包装成一个已解决(resolved)状态的 Promise
。
例如:
async function simpleAsyncFunction() {
return 42;
}
simpleAsyncFunction().then(value => {
console.log(value); // 输出: 42
});
在上述代码中,simpleAsyncFunction
是一个 async
函数,它返回数字 42
。由于 42
不是 Promise
,JavaScript 引擎将其包装成一个已解决状态的 Promise
,其 resolve
值为 42
。所以通过 .then
可以获取到这个值。
二、正常返回值的处理
- 返回基本数据类型
当
async
函数返回基本数据类型,如字符串、数字、布尔值等,就像前面例子中返回数字42
一样,JavaScript 会将其包装成Promise
。这使得我们可以像处理普通Promise
一样,使用.then
方法来获取这个值。async function returnString() { return 'Hello, async!'; } returnString().then(str => { console.log(str); // 输出: Hello, async! });
- 返回对象
async
函数也可以返回对象。同样,该对象会被包装成一个已解决状态的Promise
。async function returnObject() { return {name: 'John', age: 30}; } returnObject().then(obj => { console.log(obj.name); // 输出: John console.log(obj.age); // 输出: 30 });
- 返回数组
数组作为返回值也遵循相同的规则。
async function returnArray() { return [1, 2, 3]; } returnArray().then(arr => { console.log(arr[0]); // 输出: 1 });
三、返回 Promise 对象的处理
- 返回已解决状态的 Promise
async
函数可以直接返回一个已解决状态的Promise
。在这种情况下,async
函数的行为与直接返回值类似,只是Promise
已经是外部创建好的。
这里async function returnResolvedPromise() { return Promise.resolve('Resolved value'); } returnResolvedPromise().then(value => { console.log(value); // 输出: Resolved value });
returnResolvedPromise
函数返回了一个已解决的Promise
,async
函数会直接传递这个Promise
的解决值,就好像它是直接返回的普通值一样。 - 返回已拒绝状态的 Promise
当
async
函数返回一个已拒绝状态的Promise
时,我们可以通过.catch
方法来捕获拒绝原因。
这种情况下,async function returnRejectedPromise() { return Promise.reject('Rejection reason'); } returnRejectedPromise().catch(reason => { console.log(reason); // 输出: Rejection reason });
async
函数返回的Promise
会直接进入拒绝状态,并且将拒绝原因传递给.catch
块。 - 处理复杂的 Promise 链返回
async
函数内部可能涉及复杂的Promise
链操作,最后返回一个Promise
。
在这个例子中,async function complexPromiseChain() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('First resolve'); }, 1000); }) .then(value => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(value +'and Second resolve'); }, 1000); }); }); } complexPromiseChain().then(finalValue => { console.log(finalValue); // 输出: First resolve and Second resolve });
complexPromiseChain
函数内部创建了一个Promise
链,第一个Promise
在 1 秒后解决,然后其解决值作为第二个Promise
的输入,第二个Promise
又在 1 秒后解决。最终async
函数返回的是整个Promise
链的最终解决值。
四、错误处理与返回值
- 使用 try - catch 捕获错误
在
async
函数内部,如果发生错误,可以使用传统的try - catch
块来捕获。
在上述代码中,async function errorInAsyncFunction() { try { throw new Error('Internal error'); } catch (error) { console.log('Caught error:', error.message); return 'Error handled, returning a value'; } } errorInAsyncFunction().then(value => { console.log(value); // 输出: Error handled, returning a value });
async
函数内部抛出了一个错误,通过try - catch
捕获后,catch
块中可以进行错误处理,并返回一个值。这个返回值同样会被包装成已解决状态的Promise
。 - 未捕获错误导致 Promise 拒绝
如果
async
函数内部发生错误,且没有被try - catch
捕获,那么返回的Promise
会进入拒绝状态。
这里async function uncaughtErrorInAsyncFunction() { throw new Error('Uncaught error'); } uncaughtErrorInAsyncFunction().catch(error => { console.log('Caught uncaught error:', error.message); });
async
函数直接抛出了一个错误,没有使用try - catch
捕获,所以返回的Promise
进入拒绝状态,通过.catch
可以捕获到这个错误。 - 处理异步操作中的错误
当
async
函数内部执行异步操作(如fetch
)发生错误时,同样可以通过try - catch
或.catch
来处理。
在这个async function asyncOperationWithError() { try { const response = await fetch('https://nonexistenturl.com'); const data = await response.json(); return data; } catch (error) { console.log('Error in fetch:', error.message); return null; } } asyncOperationWithError().then(data => { console.log(data); // 如果发生错误,输出 null });
fetch
操作的例子中,如果fetch
的 URL 不存在,会抛出错误,try - catch
捕获并处理错误,返回null
。如果不使用try - catch
,则需要在调用asyncOperationWithError
函数的地方通过.catch
来捕获错误。
五、多个 async 函数的返回值处理
- 并行执行多个 async 函数
使用
Promise.all
可以并行执行多个async
函数,并处理它们的返回值。async function asyncFunction1() { return 'Result of asyncFunction1'; } async function asyncFunction2() { return 'Result of asyncFunction2'; } async function parallelExecution() { const results = await Promise.all([asyncFunction1(), asyncFunction2()]); console.log(results[0]); // 输出: Result of asyncFunction1 console.log(results[1]); // 输出: Result of asyncFunction2 } parallelExecution();
Promise.all
接受一个Promise
数组(这里async
函数返回的都是Promise
),当所有Promise
都解决时,Promise.all
返回的Promise
才会解决,其解决值是一个包含所有Promise
解决值的数组。 - 串行执行多个 async 函数
可以通过链式调用
async
函数来实现串行执行。
在这个例子中,async function asyncFunctionA() { return 'Result of asyncFunctionA'; } async function asyncFunctionB(resultA) { return resultA +'and Result of asyncFunctionB'; } async function serialExecution() { const resultA = await asyncFunctionA(); const finalResult = await asyncFunctionB(resultA); console.log(finalResult); // 输出: Result of asyncFunctionA and Result of asyncFunctionB } serialExecution();
asyncFunctionA
先执行,其返回值作为asyncFunctionB
的输入,从而实现了串行执行。 - 处理部分失败的情况(Promise.race 和 Promise.allSettled)
- Promise.race:
Promise.race
接受一个Promise
数组,只要数组中的任何一个Promise
解决或拒绝,Promise.race
返回的Promise
就会解决或拒绝。
async function asyncFunctionFast() { return 'Fast result'; } async function asyncFunctionSlow() { return new Promise((resolve) => { setTimeout(() => { resolve('Slow result'); }, 2000); }); } async function raceExecution() { const result = await Promise.race([asyncFunctionFast(), asyncFunctionSlow()]); console.log(result); // 输出: Fast result } raceExecution();
- Promise.allSettled:
Promise.allSettled
接受一个Promise
数组,当所有Promise
都已解决(resolved)或已拒绝(rejected)时,Promise.allSettled
返回的Promise
才会解决,其解决值是一个包含每个Promise
结果(无论是解决还是拒绝)的数组。
在这个例子中,async function asyncFunctionResolved() { return 'Resolved value'; } async function asyncFunctionRejected() { throw new Error('Rejected reason'); } async function allSettledExecution() { const results = await Promise.allSettled([asyncFunctionResolved(), asyncFunctionRejected()]); results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`Promise ${index} resolved with value:`, result.value); } else { console.log(`Promise ${index} rejected with reason:`, result.reason); } }); } allSettledExecution();
Promise.allSettled
可以获取到每个async
函数返回的Promise
的最终状态,无论是解决还是拒绝。 - Promise.race:
六、与其他异步机制结合的返回值处理
- 与 setTimeout 结合
async
函数常与setTimeout
结合使用,以模拟异步操作。
这里async function asyncWithSetTimeout() { return new Promise((resolve) => { setTimeout(() => { resolve('Timeout resolved'); }, 1500); }); } asyncWithSetTimeout().then(value => { console.log(value); // 输出: Timeout resolved });
async
函数内部使用setTimeout
创建了一个异步操作,1.5 秒后Promise
解决,返回值可以通过.then
获取。 - 与回调函数结合(将回调转换为 Promise)
在 JavaScript 中,有些旧的 API 仍然使用回调函数。我们可以将回调函数转换为
Promise
,并在async
函数中使用。
在这个例子中,function oldCallbackAPI(callback) { setTimeout(() => { callback(null, 'Callback result'); }, 1000); } function convertToPromise() { return new Promise((resolve, reject) => { oldCallbackAPI((error, result) => { if (error) { reject(error); } else { resolve(result); } }); }); } async function useConvertedPromise() { const result = await convertToPromise(); console.log(result); // 输出: Callback result } useConvertedPromise();
oldCallbackAPI
是一个使用回调的旧 API,convertToPromise
函数将其转换为Promise
,然后在async
函数useConvertedPromise
中可以方便地使用。 - 与 Generator 函数结合(历史渊源与过渡)
在
async
函数出现之前,Generator 函数是处理异步操作的一种方式。虽然现在async
函数更常用,但了解它们之间的关系有助于深入理解异步编程。
在这个例子中,function* generatorFunction() { yield 1; yield 2; yield 3; } function convertGeneratorToAsync(generator) { return new Promise((resolve, reject) => { const iterator = generator(); function step(value) { const result = iterator.next(value); if (result.done) { resolve(result.value); } else { Promise.resolve(result.value).then(nextValue => { step(nextValue); }).catch(error => { reject(error); }); } } step(); }); } async function useGeneratorAsync() { const result = await convertGeneratorToAsync(generatorFunction()); console.log(result); // 输出: undefined(因为最后没有返回值) } useGeneratorAsync();
generatorFunction
是一个 Generator 函数,convertGeneratorToAsync
函数将其转换为可以通过async
函数使用的形式。async
函数可以看作是 Generator 函数的语法糖,它更简洁、易用,并且内置了对Promise
的支持。
七、实际应用场景中的返回值处理
- 数据获取与处理
在前端开发中,经常需要从 API 获取数据并进行处理。
这里async function fetchUserData() { try { const response = await fetch('https://example.com/api/user'); const data = await response.json(); return data.name; } catch (error) { console.log('Error fetching user data:', error.message); return null; } } fetchUserData().then(name => { if (name) { console.log('User name:', name); } });
fetchUserData
函数通过fetch
获取用户数据,将其解析为 JSON 格式,并返回用户名。如果发生错误,返回null
。 - 文件操作(Node.js 环境)
在 Node.js 中,
async
函数常用于文件操作。
在这个例子中,const fs = require('fs'); const util = require('util'); async function readFileAsync() { try { const data = await util.promisify(fs.readFile)('example.txt', 'utf8'); return data; } catch (error) { console.log('Error reading file:', error.message); return null; } } readFileAsync().then(content => { if (content) { console.log('File content:', content); } });
readFileAsync
函数使用util.promisify
将 Node.js 的回调式fs.readFile
方法转换为Promise
形式,读取文件内容并返回。如果发生错误,返回null
。 - 数据库操作(以 MongoDB 为例)
在使用 MongoDB 进行数据库操作时,
async
函数也很常用。
在这个 MongoDB 操作的例子中,const { MongoClient } = require('mongodb'); async function findUserInDB() { const uri = "mongodb://localhost:27017"; const client = new MongoClient(uri); try { await client.connect(); const database = client.db('test'); const users = database.collection('users'); const user = await users.findOne({name: 'John'}); return user; } catch (error) { console.log('Error querying database:', error.message); return null; } finally { await client.close(); } } findUserInDB().then(user => { if (user) { console.log('User found in database:', user); } });
findUserInDB
函数连接到数据库,查询名为John
的用户并返回。如果发生错误,返回null
。最后通过finally
块关闭数据库连接。
通过以上对 async
函数返回值处理的全面深入探讨,我们可以更好地在各种异步编程场景中使用 async
函数,处理其返回值,以实现健壮、高效的 JavaScript 代码。无论是简单的数据返回,还是复杂的异步操作组合,都能通过合适的方法进行有效的处理。