ES6承诺和PEP3148期货的连锁差异

2024-06-02 09:18:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我对ES6承诺和PEP3148期货在执行上的差异的推理有点困惑。在Javascript中,当Promise被另一个Promise解析时,“outer”Promise一旦被解析或拒绝,就会继承“inner”Promise的值。在Python中,“外部”未来会立即用“内部”未来本身来解决,而不是用它的最终值来解决,这就是问题所在。

为了说明这一点,我为两个平台提供了两个代码片段。在Python中,代码如下所示:

import asyncio

async def foo():
    return asyncio.sleep(delay=2, result=42)

async def bar():
    return foo()

async def main():
    print(await bar())

asyncio.get_event_loop().run_until_complete(main())

在Javascript中,完全等效的代码是:

^{pr2}$

为了完整性而提供的sleep函数。

Javascript代码按预期打印42。Python代码打印<coroutine object foo at 0x102a05678>,并抱怨“coroutine'foo'从未等待”。

这样,JS允许您通过立即await承诺或让调用者等待承诺来选择控件将从当前执行上下文中传递的时间点。Python实际上除了始终awaitFuture/coroutine之外没有其他选择,因为否则您将不得不自己用一个难看的包装器函数来展开未来链:

async def unwind(value):
    while hasattr(value, '__await__'):
        value = await value

    return value

所以,问题是:这个决定背后有什么理由吗?为什么Python不允许连锁期货?有没有讨论过?有什么办法可以让行为更符合承诺吗?


Tags: 代码asyncioasyncreturnfoovaluemaindef
1条回答
网友
1楼 · 发布于 2024-06-02 09:18:06

让我来快速比较一下JavaScript的承诺Python的未来,在这里我可以指出主要的用例并揭示 决策背后的原因。在

我将使用以下虚拟示例演示异步函数的用法:

async function concatNamesById(id1, id2) {
  return (await getNameById(id1)) + ', ' + (await getNameById(id2));
}

异步JavaScript

早在承诺概念出现之前,人们就用回调编写代码。关于 哪个参数应该是回调,应该如何处理错误,等等。。。最后,我们的函数如下所示:

^{pr2}$

这与示例相同,是的,我故意使用4个缩进空格来放大所谓的回调hellpyramid of doom。使用JavaScript的人写这样的代码已经很多年了!在

然后Kris Kowal came with his flaming Q library并通过引入承诺的概念拯救了失望的JavaScript社区。 名称故意不是“未来”或“任务”。承诺概念的主要目标是摆脱金字塔。为了实现这个承诺 有一个^{}方法,它不仅允许您订阅在获得承诺值时激发的事件,而且还将 返回另一个承诺,允许链接。这就是为什么承诺和未来是一个不同的概念。承诺就多一点。在

// using chained promises
function concatNamesById(id1, id2) {
  var name1;
  return getNameById(id1).then(function(temp) {
    name1 = temp;
    return getNameById(id2); // Here we return a promise from 'then'
  }) // this then returns a new promise, resolving to 'getNameById(id2)', allows chaining
  .then(function(name2) {    
    return name1 + ', ' + name2; // Here we return an immediate value from then
  }); // the final then also returns a promise, which is ultimately returned
}

看到了吗?打开从then回调返回的承诺以构建一个干净、透明的链是非常重要的。(我自己写了这种异步 代码超过一年。) 然而,当您需要一些控制流(如条件分支或循环)时,事情会变得复杂。 当第一个ES6编译器/transpiler(如6to5)出现时,人们慢慢开始使用生成器。ES6发电机是双向的,这意味着 生成器不仅生成值,而且可以在每次迭代时接收提供的值。这使得我们可以编写以下代码:

// using generators and promises
const concatNamesById = Q.async(function*(id1, id2) {
  return (yield getNameById(id1)) + ', ' + (yield getNameById(id2));
});

仍然使用promises,^{}从生成器生成一个异步函数。这里没有黑魔法,这个包装器函数是用 只有promise.then(或多或少)。我们快到了。在

今天,由于the ES7 specification for async-await相当成熟,任何人都可以使用BabelJS将异步ES7代码编译为ES5。在

// using real async-await
async function concatNamesById(id1, id2) {
  return (await getNameById(id1)) + ', ' + (await getNameById(id2));
}

从异步函数返回promise

所以这是可行的:

async foo() {
  return /* await */ sleep('bar', 1000);
  // No await is needed!
}

这也是:

async foo() {
  return await await await 'bar';
  // You can write await pretty much everywhere you want!
}

这种弱/动态/鸭子类型非常适合JavaScript的世界观。在

你是对的,你可以从一个异步函数返回一个promise而不需要等待,而且它会被取消。 这并不是一个真正的决定,而是promise.then如何工作的直接结果,因为它会释放 承诺让链子更舒服。不过,我认为在每一个 async call,让您清楚地知道该调用是异步的。我们确实有多个bug 每天因为错过等待关键词,因为它们不会造成瞬间的错误,只是一串 并行运行的随机任务。我喜欢调试它们。说真的。在

异步Python

让我们看看在Python中引入async await协同例程之前,Python用户做了什么:

def concatNamesById(id1, id2):
  return getNameById(id1) + ', ' + getNameById(id2);

等等什么?未来在哪里?回叫金字塔在哪里?关键是Python人 没有JavaScript用户遇到的任何问题。他们只是用阻塞电话。在

JavaScript和Python的巨大区别

那么为什么JavaScript用户不使用阻塞调用呢?因为他们不能!嗯,他们想去。相信我。 在他们引入WebWorkers之前,所有的JavaScript代码都运行在gui线程上,并导致任何阻塞调用 用户界面冻结!这是不受欢迎的,所以编写规范的人尽一切努力防止这种情况发生。 到今天为止,在我所知的浏览器中阻止UI线程的唯一方法是:

  • 使用X带有不推荐使用的async = false选项的MLHttpRequest
  • 使用旋转等待
  • (或进行大量计算)

只是用JavaScript实现锁的方式是不一样的。 (直到浏览器供应商开始实现Shared Array Buffers之类的东西,我担心这会带来 一旦狂热的业余爱好者开始使用它们,我们就会受到一种特殊的诅咒)

另一方面,在Python中阻塞调用没有错,因为通常没有 “gui线程”。如果您还需要一些并行性,可以启动一个新线程,然后继续工作。这在以下情况下很有用 您希望一次运行多个SOAP请求,但是当您想利用计算能力时,就没有那么有用了 在你的笔记本电脑的所有cpu核心中,Global Interpreter Lock会阻止你这么做。(这是 由multiprocessing模块处理过,但这是另一个故事)

那么,为什么Python人需要协同程序呢?主要的答案是反应式编程在当今非常流行。 当然还有其他方面,比如不想为每个restful查询启动一个新线程(一些 众所周知,Python库会泄漏线程id,直到它们最终崩溃),或者只是想删除所有 不必要的多线程原语,如互斥体和信号量。(我的意思是,如果 代码可以重写为协同程序。当然,当你进行多线程处理时,它们是需要的,这就是未来的原因 被开发出来了。在

Python的未来不允许任何形式的链接。他们不被使用的方式。记住,JavaScript的 承诺要把金字塔计划变成一个很好的连锁计划,所以解除是必要的。但自动 展开需要编写特定的代码,并且需要将来的解决方案来区分所提供的 值的类型或属性。也就是说,它将更加复杂(==更难调试),并且 向弱类型化迈进了一小步,这违背了python的主要原则。Python的未来是轻量级的, 简洁易懂。他们不需要自动解卷。在

相关问题 更多 >