Python3 中的 Futures 和 ES6 中的 Promise 的区别

Posted

技术标签:

【中文标题】Python3 中的 Futures 和 ES6 中的 Promise 的区别【英文标题】:Differences between Futures in Python3 and Promises in ES6 【发布时间】:2017-11-26 05:31:26 【问题描述】:

从 Python 3.5 开始,该语言引入了关键字 awaitasync。现在,我更像是一个使用 Python 2.7 的人,而且我已经避免使用 Python 3 很长一段时间了,所以asyncio 对我来说很陌生。根据我的理解,await/async 的工作方式似乎与它们在 ES6(或 javascript、ES2015,无论你想如何称呼它)中的工作方式非常相似。

这是我制作的两个脚本来比较它们。

import asyncio

async def countdown(n):
    while n > 0:
        print(n)
        n -= 1
        await asyncio.sleep(1)

async def main():
    """Main, executed in an event loop"""

    # Creates two countdowns
    futures = asyncio.gather(
        countdown(3), 
        countdown(2)
    )
    # Wait for all of them to finish
    await futures
    # Exit the app
    loop.stop()

loop = asyncio.get_event_loop()
asyncio.ensure_future(main())
loop.run_forever()
function sleep(n)
    // ES6 does not provide native sleep method with promise support
    return new Promise(res => setTimeout(res, n * 1000));


async function countdown(n)
    while(n > 0)
        console.log(n);
        n -= 1;
        await sleep(1);
    


async function main()
    // Creates two promises
    var promises = Promise.all([
        countdown(3),
        countdown(2)
    ]);
    // Wait for all of them to finish
    await promises;
    // Cannot stop interpreter's event loop


main();

需要注意的一点是代码非常相似,而且它们的工作方式几乎相同。

以下是问题:

    在 Python 和 ES6 中,await/async 都是基于生成器的。认为 Futures 与 Promises 相同是否正确?

    我已经看到asyncio 文档中使用的术语TaskFutureCoroutine。它们之间有什么区别?

    我应该开始编写始终运行事件循环的 Python 代码吗?

【问题讨论】:

【参考方案1】:
    在 Python 和 ES6 中,await/async 都是基于生成器的。认为 Futures 与 Promises 相同是否正确?

不是Future,而是Python的Task大致相当于Javascript的Promise。请参阅下面的更多详细信息。

    我已经看到asyncio 文档中使用的术语TaskFutureCoroutine。它们之间有什么区别?

它们是完全不同的概念。 Task主要由FutureCoroutine组成。让我们简要描述一下这些原语(我将简化很多事情以仅描述主要原理):

未来

未来只是价值的抽象,可能尚未计算,最终将可用。这是一个简单的容器,只做一件事 - 每当设置值时,触发所有注册的回调。

如果你想获得那个值,你可以通过add_done_callback()方法注册一个回调。

但与Promise 不同的是,实际计算是在外部完成的——外部代码必须调用set_result() 方法来解决未来问题。

协程

协程是与Generator非常相似的对象。

生成器通常在for 循环内迭代。它产生值,并且从PEP342接受开始,它接收值。

协程通常在 asyncio 库深处的事件循环中迭代。协程产生Future 实例。当你迭代一个协程并且它产生一个未来时,你应该等到这个未来被解决。之后你应该send将future的值加入协程,然后你会收到另一个future,以此类推。

await 表达式实际上与 yield from 表达式相同,因此通过等待其他协程,您会停止,直到该协程的所有未来都已解决,并获得协程的返回值。 Future 是一次性可迭代的,它的迭代器返回实际的 Future - 这大致意味着 await future 等于 yield from future 等于 yield future

任务

Task 是 Future 已经实际开始计算并附加到事件循环。所以它是一种特殊的 Future(类Task 派生自类Future),它与一些事件循环相关联,它有一些协程,作为任务执行器

Task 通常由事件循环对象创建:你给循环一个协程,它创建 Task 对象并开始以上述方式迭代该协程。协程完成后,Task 的 Future 由协程的返回值解析。

你看,这个任务与 JS Promise 非常相似——它封装了后台作业及其结果。

协程函数和异步函数

Coroutine func 是一个协程工厂,类似于生成器的生成器函数。请注意 Python 的协程函数和 Javascript 的异步函数之间的区别——JS 异步函数在调用时会创建一个 Promise,并且其内部生成器会立即开始迭代,而 Python 的协程什么都不做,直到在其上创建 Task。

    我应该开始编写始终运行事件循环的 Python 代码吗?

如果您需要任何异步功能,那么您应该这样做。事实证明,很难混合同步和异步代码——你的整个程序最好是异步的(但你可以通过 asyncio 线程池 API 在单独的线程中启动同步代码块)

【讨论】:

以上是关于Python3 中的 Futures 和 ES6 中的 Promise 的区别的主要内容,如果未能解决你的问题,请参考以下文章

Python 3 中的 Concurrent.futures 与多处理

Python3模块concurrent.futures模块,线程池进程池

Python3标准库:concurrent.futures管理并发任务池

python3 线程池-threadpool模块与concurrent.futures模块

python 期物

使用concurrent.futures模块并发,实现进程池线程池