在 Python 中,async
和 await
是用于异步编程的关键字,它们引入了异步/协程(coroutine)的概念。
async
关键字用于定义一个异步函数,表明该函数是一个协程,可以在其中使用 await
关键字等待其他异步操作完成。异步函数的执行不会阻塞事件循环,而是会立即返回一个协程对象。
await
关键字用于在异步函数内部等待其他协程执行完成,后面通常跟着一个需要等待的协程对象。当遇到 await
关键字时,事件循环会挂起当前的协程,并执行其他任务,直到等待的协程完成后才会恢复执行当前协程。
使用 async
和 await
的主要作用包括:
- 提高程序效率:异步编程可以充分利用 I/O 等待时间,使得程序在等待操作完成时能够继续执行其他任务,从而提高了整体程序的效率。
- 改善用户体验:在网络编程等场景中,异步操作可以避免阻塞用户界面,使得应用程序更加流畅。
- 简化并发编程:通过使用
async
和await
关键字,可以更方便地编写并发程序,避免了传统多线程编程中的锁和同步问题。
以下是一个简单的 Python 异步编程示例代码:
import asyncio
import time
async def visit_url(url, response_time):
"""访问 url"""
await asyncio.sleep(response_time)
return f"访问 {url}, 已得到返回结果"
async def main():
start_time = time.perf_counter()
task1 = visit_url('http://example.com/1', 2)
task2 = visit_url('http://example.com/2', 3)
await asyncio.gather(task1, task2)
print(f"消耗时间:{time.perf_counter() - start_time}")
asyncio.run(main())
在上述代码中:
visit_url
是一个异步函数,它使用asyncio.sleep(response_time)
模拟了一个耗时的 I/O 操作(实际应用中可能是网络请求、文件读取等)。main
函数中创建了两个visit_url
的任务task1
和task2
,然后使用asyncio.gather
并发执行这两个任务。asyncio.gather
会等待所有给定的协程完成,并返回它们的结果。
通过使用异步编程,当 task1
遇到 await asyncio.sleep(2)
时,它会暂停并将控制权交回给事件循环,事件循环会接着执行 task2
,从而实现了并发效果,总的耗时接近较长的那个 I/O 操作的时间(3 秒左右),而不是两个 I/O 操作的时间总和(5 秒)。
另外,还可以使用 asyncio.create_task
来创建任务,示例如下:
async def main():
coro1 = visit_url('http://example.com/1', 2)
coro2 = visit_url('http://example.com/2', 3)
task1 = asyncio.create_task(coro1)
task2 = asyncio.create_task(coro2)
await task1
await task2
这样也能实现与 asyncio.gather
类似的并发效果。
协程的主要应用场景是 I/O 密集型任务,例如网络请求(如使用 aiohttp
库)、文件读取(如 aiofile
)、Web 框架(如 aiohttp
、FastAPI
)、数据库查询(如 asyncpg
、databases
等)。
接下来我们看Javascript中的
“async”通常指的是异步编程中的“async 函数”(async function)。它是 ES2017(ECMAScript 2017)中引入的新特性,用于更方便地编写异步操作代码。
async 函数的主要特点和用法包括:
- 返回 Promise 对象:async 函数的返回值是一个 Promise 对象。如果在 async 函数中直接返回一个值,async 会把这个值通过
Promise.resolve()
封装成 Promise 对象返回。 - 使用 await 等待异步操作:在 async 函数内部,可以使用
await
关键字来等待一个异步操作的完成。await
后面通常跟着一个 Promise 对象,如果等待的是一个 Promise 对象,它会暂停 async 函数的执行,直到该 Promise 对象状态变为 resolved,并获取其结果作为await
表达式的值,然后继续执行 async 函数后面的代码。如果await
后面是其他非 Promise 值,则直接返回该值本身。 - 非阻塞执行:async 函数的调用不会造成阻塞,它内部的异步操作会被封装在一个 Promise 对象中异步执行。
例如,下面是一个使用 async/await 实现异步操作的示例:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function asyncTask() {
console.log('开始执行异步任务');
await delay(3000);
console.log('异步任务完成,3 秒后输出');
}
asyncTask();
在上述示例中,asyncTask
是一个 async 函数,其中使用 await delay(3000)
来等待 delay
函数返回的 Promise 对象状态变为 resolved,从而实现了暂停 3 秒后再继续执行后面代码的效果。
async/await 使得异步代码看起来更像同步代码,提高了代码的可读性和可维护性,避免了传统的回调函数嵌套或 Promise.then 链的复杂写法。但需要注意的是,await
关键字只能在 async 函数内部使用,如果在普通函数中使用会导致语法错误。同时,为了处理异步操作可能出现的错误,通常会将 await 表达式放在 try/catch 结构中。
Python 和 JavaScript 中的 async
都用于实现异步编程,但它们之间有一些区别:
- 执行方式:
- 在 Python 中,
async
和await
通常与asyncio
库一起使用,通过事件循环来管理异步任务的执行。而异步任务需要通过async
关键字定义为协程函数,并使用await
关键字来等待异步操作的完成。 - 在 JavaScript 中,
async
和await
是基于 Promise 的异步操作的语法糖。使用async
关键字定义的函数会返回一个 Promise 对象,可以使用await
关键字来等待 Promise 的解决。
- 在 Python 中,
- 应用场景:
- Python 中的异步编程主要用于处理 I/O 密集型任务,如网络请求、文件读写等,可以提高程序的性能和响应性。
- JavaScript 中的异步编程则广泛应用于前端开发中,如处理 Ajax 请求、动画效果、事件处理等,以及在 Node.js 中进行服务器端编程,处理文件操作、数据库查询等 I/O 密集型任务。
- 错误处理:
- 在 Python 中,可以使用
try/except
语句来捕获异步操作中的异常。 - 在 JavaScript 中,可以使用
try/catch
块来捕获await
表达式抛出的异常,或者使用.catch()
方法来处理 Promise 的拒绝。
- 在 Python 中,可以使用
总的来说,Python 和 JavaScript 中的 async
都为异步编程提供了更简洁和直观的方式,但在具体的使用和应用场景上可能会有所不同。