Python3 中的 Future 和 ES6 中的 Promises 的区别
Differences between Futures in Python3 and Promises in ES6
自 Python 3.5 起,关键字 await
和 async
被引入该语言。现在,我更像是一个 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
文档中看到了术语 Task
、Future
和 Coroutine
。它们有什么区别?
我是否应该开始编写 Python 始终具有事件循环的代码 运行?
- In both Python and ES6,
await/async
are based on generators. Is it a correct to think Futures are the same as Promises?
不是Future
,但Python的Task
大致相当于Javascript的Promise
。请参阅下面的更多详细信息。
- I have seen the terms
Task
, Future
and Coroutine
used in the asyncio
documentation. What are the differences between them?
它们是完全不同的概念。 Task
主要由Future
和Coroutine
组成。让我们简要描述一下这些原语(我将简化很多事情以仅描述主要原理):
未来
Future 只是对值的抽象,可能尚未计算但最终会可用。这是一个只做一件事的简单容器——每当设置值时,触发所有已注册的回调。
如果你想获得那个值,你通过add_done_callback()
方法注册一个回调。
但与 Promise
不同的是,实际计算是在外部完成的 - 外部代码必须调用 set_result()
方法来解析未来。
协程
协程是与Generator
非常相似的对象。
生成器通常在 for
循环中迭代。它 产生 值,并且从 PEP342 接受开始,它 接收 值。
协程通常在 asyncio
库深处的事件循环中迭代。协程产生 Future
个实例。当你迭代一个协程并产生一个未来时,你应该等到这个未来被解决。之后你应该 send
未来的价值进入协程,然后你收到另一个未来,依此类推。
await
表达式实际上与 yield from
表达式相同,因此通过 等待 其他协程,您将停止,直到该协程的所有未来都已解决,并获取协程的 return 值。 Future
是一次迭代,它的迭代器 return 是实际的 Future - 这大致意味着 await future
等于 yield from future
等于 yield future
。
任务
Task是实际开始计算的Future,附加到事件循环中。所以它是一种特殊的 Future(class Task
派生自 class Future
),它与一些事件循环相关联,并且它有一些协程,用作 任务执行者.
任务通常由事件循环对象创建:您为循环提供协程,它创建任务对象并开始以上述方式迭代该协程。协程完成后,Task 的 Future 由协程的 return 值解决。
你看,任务与 JS Promise 非常相似 - 它封装了后台作业及其结果。
协程函数和异步函数
Coroutine func 是一个协程工厂,就像生成器函数之于生成器。注意 Python 的协程函数和 Javascript 的异步函数之间的区别 - JS 异步函数在被调用时会创建一个 Promise 并且其内部生成器会立即开始迭代,而 Python' s 协程什么都不做,直到在其上创建任务。
- Should I start writing Python code that always has an event loop running?
如果您需要任何 asyncio 功能,那么您应该这样做。事实证明,很难混合使用同步和异步代码——你的整个程序最好是异步的(但你可以通过 asyncio 线程池 API)
在单独的线程中启动同步代码块
我看到了下游的主要区别。
const promise = new Promise((resolve, reject) => sendRequest(resolve, reject));
await promise;
在JavaScript中,resolve
和reject
两个函数是由JS 引擎创建的,它们必须传递给您以便您跟踪它们。最后,大部分时间您仍在使用两个回调函数,并且 Promise 在 doStuff
调用 resolve
之后实际上不会做超过 setTimeout(() => doMoreStuff())
的事情。一旦调用回调,就无法检索旧结果或 Promise 的状态。 Promise 主要只是常规调用和 async/await 之间的胶水代码(因此您可以在其他地方等待 promise)和一些用于链接 .then
s.
的错误回调转发
future = asyncio.Future()
sendRequest(future)
await future
在 Python 中,Future 本身成为返回结果的接口,并跟踪结果。
由于 Andril 给出了最接近 Python 等价于 JavaScript 的 Promise(即 Task;你给它一个回调并等待它完成),我想去另一种方式。
class Future {
constructor() {
this.result = undefined;
this.exception = undefined;
this.done = false;
this.success = () => {};
this.fail = () => {};
}
result() {
if (!this.done) {
throw Error("still pending");
}
return this.result();
}
exception() {
if (!this.done) {
throw Error("still pending");
}
return this.exception();
}
setResult(result) {
if (this.done) {
throw Error("Already done");
}
this.result = result;
this.done = true;
this.success(this.result);
}
setException(exception) {
if (this.done) {
throw Error("Already done");
}
this.exception = exception;
this.done = true;
this.fail(this.exception);
}
then(success, fail) {
this.success = success;
this.fail = fail;
}
}
JS await 基本上生成两个传递给 .then
的回调,其中在 JS Promise 中应该发生实际逻辑。在许多示例中,您会在此处找到 setTimeout(resolve, 10000)
来演示跳出事件循环,但如果您改为跟踪这两个回调,则可以随心所欲地使用它们..
function doStuff(f) {
// keep for some network traffic or so
setTimeout(() => f.setResult(true), 3000);
}
const future = new Future();
doStuff(future);
console.log('still here');
console.log(await future);
上面的例子证明了; 'still here' 三秒后你得到 'true'.
如你所见,不同之处在于 Promise 接收一个工作函数并在内部处理 resolve 和 reject,而 Future 不将工作内部化,只关心回调。就个人而言,我更喜欢 Future,因为它少了一层回调地狱——这是 Promises 的首要原因之一:回调链。
自 Python 3.5 起,关键字 await
和 async
被引入该语言。现在,我更像是一个 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
文档中看到了术语Task
、Future
和Coroutine
。它们有什么区别?我是否应该开始编写 Python 始终具有事件循环的代码 运行?
- In both Python and ES6,
await/async
are based on generators. Is it a correct to think Futures are the same as Promises?
不是Future
,但Python的Task
大致相当于Javascript的Promise
。请参阅下面的更多详细信息。
- I have seen the terms
Task
,Future
andCoroutine
used in theasyncio
documentation. What are the differences between them?
它们是完全不同的概念。 Task
主要由Future
和Coroutine
组成。让我们简要描述一下这些原语(我将简化很多事情以仅描述主要原理):
未来
Future 只是对值的抽象,可能尚未计算但最终会可用。这是一个只做一件事的简单容器——每当设置值时,触发所有已注册的回调。
如果你想获得那个值,你通过add_done_callback()
方法注册一个回调。
但与 Promise
不同的是,实际计算是在外部完成的 - 外部代码必须调用 set_result()
方法来解析未来。
协程
协程是与Generator
非常相似的对象。
生成器通常在 for
循环中迭代。它 产生 值,并且从 PEP342 接受开始,它 接收 值。
协程通常在 asyncio
库深处的事件循环中迭代。协程产生 Future
个实例。当你迭代一个协程并产生一个未来时,你应该等到这个未来被解决。之后你应该 send
未来的价值进入协程,然后你收到另一个未来,依此类推。
await
表达式实际上与 yield from
表达式相同,因此通过 等待 其他协程,您将停止,直到该协程的所有未来都已解决,并获取协程的 return 值。 Future
是一次迭代,它的迭代器 return 是实际的 Future - 这大致意味着 await future
等于 yield from future
等于 yield future
。
任务
Task是实际开始计算的Future,附加到事件循环中。所以它是一种特殊的 Future(class Task
派生自 class Future
),它与一些事件循环相关联,并且它有一些协程,用作 任务执行者.
任务通常由事件循环对象创建:您为循环提供协程,它创建任务对象并开始以上述方式迭代该协程。协程完成后,Task 的 Future 由协程的 return 值解决。
你看,任务与 JS Promise 非常相似 - 它封装了后台作业及其结果。
协程函数和异步函数
Coroutine func 是一个协程工厂,就像生成器函数之于生成器。注意 Python 的协程函数和 Javascript 的异步函数之间的区别 - JS 异步函数在被调用时会创建一个 Promise 并且其内部生成器会立即开始迭代,而 Python' s 协程什么都不做,直到在其上创建任务。
- Should I start writing Python code that always has an event loop running?
如果您需要任何 asyncio 功能,那么您应该这样做。事实证明,很难混合使用同步和异步代码——你的整个程序最好是异步的(但你可以通过 asyncio 线程池 API)
在单独的线程中启动同步代码块我看到了下游的主要区别。
const promise = new Promise((resolve, reject) => sendRequest(resolve, reject));
await promise;
在JavaScript中,resolve
和reject
两个函数是由JS 引擎创建的,它们必须传递给您以便您跟踪它们。最后,大部分时间您仍在使用两个回调函数,并且 Promise 在 doStuff
调用 resolve
之后实际上不会做超过 setTimeout(() => doMoreStuff())
的事情。一旦调用回调,就无法检索旧结果或 Promise 的状态。 Promise 主要只是常规调用和 async/await 之间的胶水代码(因此您可以在其他地方等待 promise)和一些用于链接 .then
s.
future = asyncio.Future()
sendRequest(future)
await future
在 Python 中,Future 本身成为返回结果的接口,并跟踪结果。
由于 Andril 给出了最接近 Python 等价于 JavaScript 的 Promise(即 Task;你给它一个回调并等待它完成),我想去另一种方式。
class Future {
constructor() {
this.result = undefined;
this.exception = undefined;
this.done = false;
this.success = () => {};
this.fail = () => {};
}
result() {
if (!this.done) {
throw Error("still pending");
}
return this.result();
}
exception() {
if (!this.done) {
throw Error("still pending");
}
return this.exception();
}
setResult(result) {
if (this.done) {
throw Error("Already done");
}
this.result = result;
this.done = true;
this.success(this.result);
}
setException(exception) {
if (this.done) {
throw Error("Already done");
}
this.exception = exception;
this.done = true;
this.fail(this.exception);
}
then(success, fail) {
this.success = success;
this.fail = fail;
}
}
JS await 基本上生成两个传递给 .then
的回调,其中在 JS Promise 中应该发生实际逻辑。在许多示例中,您会在此处找到 setTimeout(resolve, 10000)
来演示跳出事件循环,但如果您改为跟踪这两个回调,则可以随心所欲地使用它们..
function doStuff(f) {
// keep for some network traffic or so
setTimeout(() => f.setResult(true), 3000);
}
const future = new Future();
doStuff(future);
console.log('still here');
console.log(await future);
上面的例子证明了; 'still here' 三秒后你得到 'true'.
如你所见,不同之处在于 Promise 接收一个工作函数并在内部处理 resolve 和 reject,而 Future 不将工作内部化,只关心回调。就个人而言,我更喜欢 Future,因为它少了一层回调地狱——这是 Promises 的首要原因之一:回调链。