在ES6承诺与PEP3148期货链差异 [英] Chaining difference in ES6 Promises and PEP3148 Futures

查看:251
本文介绍了在ES6承诺与PEP3148期货链差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对实施ES6承诺与PEP3148期货的推理差异有些不解。在Javascript中,当承诺与另一个承诺解决,外的承诺秉承内部的承诺的价值,一旦它的解决或拒绝。在Python中,外的未来,而不是被立即用内部的未来本身,而不是与它的最终价值,这就是问题所在。解决

I am somewhat puzzled about reasoning in difference in implementation of ES6 Promises and PEP3148 Futures. In Javascript, when Promise is resolved with another Promise, "outer" promise inherits the value of "inner" promise once it's resolved or rejected. In Python, "outer" future is instead immediately resolved with "inner" future itself, not with it's eventual value, and that is the problem.

要说明这一点,我已经为这两个平台提供了两个code片段。在Python中,code是这样的:

To illustrate this, I had provided two code snippets for both platforms. In Python, code looks like this:

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中,完全等价于code是这样的:

In Javascript, fully equivalent code is this:

function sleep(delay, result) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result);
        }, delay * 1000);
    });
}

async function foo() {
    return sleep(2, 42);
}

async function bar() {
    return foo();
}

(async function main() {
    console.log(await bar());
})();

提供完整起见睡眠功能。

的Javascript code打印 42 ,符合市场预期。 Python的code打印<协程对象foo在0x102a05678方式> 和有关投诉协程'富'是从来没有期待已久的

Javascript code prints 42, as expected. Python code prints <coroutine object foo at 0x102a05678> and complaints about "coroutine 'foo' was never awaited".

这种方式,JS允许您选择当控制将被传递了你的当前执行的背景下,通过立即点等待 ING承诺,或者让呼叫者等待着他们。蟒蛇从字面上让你比永远没有其他选择,等待未来/协同程序,否则你将不得不自己与这样一个丑陋的包装功能,放松身心的未来链在一个循环:

This way, JS allows you to choose the point when the control will be passed away of your current context of execution, by immediately awaiting promises, or letting caller to await them. Python literally leaves you no other option than always await the Future/coroutine, because otherwise you will have to unwind the Future chain in a loop yourself with an ugly wrapper function like this:

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

    return value

因此​​,问题:有这个决定背后有推理?为什么Python的禁止链期货?是否有任何关于它的讨论?有没有什么可以做,以使行为匹配少辉更接近?

推荐答案

让我显示的 JavaScript的承诺的快速比较,和的 Python的期货的,在这里我可以指出主要用例和揭示
背后决定的理由。

Let me show a quick comparison of JavaScript's Promises, and Python's futures, where I can point out the main usecases and reveal the reasons behind decisions.

我会用下面的虚拟实例演示使用异步功能:

I will use the following dummy example to demonstrate the use of async functions:

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

异步JavaScript

早在一天,承诺的概念出现之前,人们使用的回调的写自己的code。大约还有各种公约
该参数应该是回调,错误应该如何处理,等等。最后,我们的函数看起来是这样的:

Asynchronous JavaScript

Back in the day, before the concept of Promises emerged, people wrote their code using callbacks. There are still various conventions about which argument should be the callback, how errors should be handled, etc... At the end, our function looks something like this:

// using callbacks
function concatNamesById(id1, id2, callback) {
    getNameById(id1, function(err, name1) {
        if (err) {
            callback(err);
        } else {
            getNameById(id2, function(err, name2) {
                if (err) {
                    callback(err);
                } else {
                    callback(null, name1 + ', ' + name2);
                }
            });
        }
    });
}

这不一样的例子,是的,我用了4缩进空格故意放大与所谓的回调地狱问题的或
厄运金字塔 。使用JavaScript人们写code这样多年!

This does the same as the example, and yes, I used 4 indentation spaces intentionally to magnify the problem with the so called callback hell or pyramid of doom. People using JavaScript were writing code like this for many years!

然后克里斯·科瓦尔带着他燃烧的Q库,并通过引入的概念,节省了失望的JavaScript社区的承诺的。
这个名字是不是故意的未来或任务。无极理念的主要目的是摆脱金字塔。为了实现这一承诺
然后 方法,它不仅可以让你订阅获得约定的价值时所触发的事件,但也会
返回另一个承诺,允许的链接的。这就是做出承诺和期货不同的概念。承诺是多一点。

Then Kris Kowal came with his flaming Q library and saved the disappointed JavaScript community by introducing the concept of Promises. The name is intentionally not "future" or "task". The main goal of the Promise concept is to get rid of the pyramid. To achieve this promises have a then method which not only allows you to subscribe to the event that is fired when the promised value is obtained, but will also return another promise, allowing chaining. That is what make Promises and futures a different concept. Promises are a bit more.

// 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
}

看到了吗?重要的是要解开从返回承诺再回调建设廉洁,透明的链条。 (我自己写的这种asynchronouos的
code一年多了。)
然而,事情变得复杂,当你需要这样的条件分支或循环的一些控制流。
当ES6第一个编译器/ transpilers(如6to5)出现了,人们慢慢开始使用发电机。 ES6发电机是两个方向,意义
发电机的不仅生产值,但可以在每次迭代接收提供的值。这使我们能够写出下面的code:

See? It is essential to unwrap promises returned from then callbacks to build a clean, transparent chain. (I myself wrote this kind of asynchronouos code for over a year.) However, things get complicated when you need some control flow like conditional branching or loops. When the first compilers/transpilers (like 6to5) for ES6 appeared, people slowly started to use generators. ES6 generators are two directional, meaning the generator not only produces values, but can receive supplied values on each iteration. This allowed us to write the following code:

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

仍在使用的承诺, Q.async 使得从发电机异步功能。有没有黑魔法在那里,这个封装功能是通过实现
不过 promise.then (或多或少)。我们几乎没有。

Still using promises, Q.async makes an async function from a generator. There is no black magic there, this wrapper function is implemented using nothing but promise.then (more or less). We are nearly there.

今天,因为的ES7规范异步等待是pretty成熟,任何人都可以编译异步ES7 code。使用到ES5 <一个href=\"http://babeljs.io/repl/#?experimental=false&evaluate=false&loose=false&spec=false&$c$c=async%20function%20foo()%20%7B%0A%20%20await%20bar()%3B%0A%7D\"相对=nofollow> BabelJS 。

Today, since the ES7 specification for async-await is pretty mature, anyone can compile asynchronous ES7 code to ES5 using BabelJS.

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

从异步函数返回的承诺

所以这个作品:

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

也是如此这样的:

and so does this:

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

这种弱/动态/ duck类型在世界的JavaScript的看法非常适合。

This kind of weak/dynamic/duck typing fits well in JavaScript's view of the world.

您是正确的,你可以从一个异步函数返回一个承诺没有在等待着,它就会unwinded。
这是不是一个真正的决定,但如何 promise.then 工作的直接后果,因为它的开卷
承诺使链接舒服。不过,我认为这是一个很好的做法为写之前等待每
异步调用要想清楚你是知道电话是异步的
。我们确实有多个漏洞
每天因为思念的await关键字,因为它们不会造成即时的错误,只是一堆
并行运行随机任务。我喜欢他们的调试。认真。

You are right that you can return a promise from an async function without await, and it gets unwinded. It is not really a decision, but is a direct consequence of how promise.then works, as it unwinds the promises to make chaining comfortable. Still, I think it is a good practice to write await before every async call to make it clear you are aware of the call being asynchronous. We do have multiple bugs every day because of missing await keywords, as they will not cause instantaneous errors, just a bunch of random tasks running in parallel. I love debugging them. Seriously.

让我们看看Python的人之前做过异步协同程序等待Python中分别介绍:

Let's see what Python people did before async-await coroutines were introduced in python:

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

等待什么呢?在哪里期货?哪里回调金字塔?的问题是,人们的Python
没有任何的JavaScript的人有问题。他们只是使用阻塞调用。

Wait what? Where are the futures? Where is the callback pyramid? The point is that Python people do not have any of the problems JavaScript people had. They just use blocking calls.

那么,为什么没有人JavaScript的使用阻塞调用?因为他们无法!好了,他们想。相信我。
之前,他们推出了 WebWorkers 所有的JavaScript code RAN在GUI线程,并造成任何阻塞调用
在UI冻结!这是不理想的,这样的人写的规格所做的一切都是以prevent这样的事情。
截至今天,唯一的方法来阻止在浏览器UI线程我所知道的:

So why didn't JavaScript people use blocking calls? Because they couldn't! Well, they wanted to. Believe me. Before they introduced WebWorkers all JavaScript code ran on the gui thread, and any blocking call caused the ui to freeze! That is undesireable, so people writing the specifications did everything to prevent such things. As of today, the only ways to block the UI thread in a browser I am aware of:


  • 使用XMLHtt prequest与德precated 异步=虚假选项

  • 使用旋转等待

  • (或者实际做大量计算)

目前无法实现在JavaScript中的自旋锁和类似的事情,就是没有办法。
(直到浏览器厂商开始实施的东西如共享阵列缓冲器,对此我怕会带来
一种特殊的向我们地狱尽快业余爱好者将开始使用它们)

Currently you cannot implement spin-locks and similar things in JavaScript, there is just no way. (Until browser vendors start to implement things like Shared Array Buffers, which I am afraid of will bring a special kind of hell upon us as soon as enthusiast amateurs will start to use them)

在另一方面,有什么错在Python阻塞调用因为通常没有这样的东西
GUI线程。如果您还需要一些并行性,你就可以开始一个新的线程,并就工作。这是非常有用的时候
你想一次运行多个SOAP请求,但不能作为有用的,当你想使用的计算能力
在你的笔记本电脑中的所有CPU内核,作为全球国米preTER锁定将prevent你这样做。 (这是
工作围绕由多处理模块,但那是另一回事)

On the other hand there is nothing wrong with blocking calls in Python as usually there is no such thing as a 'gui thread'. If you still need some parallelism, you can start a new thread, and work on that. This is useful when you want to run multiple SOAP requests at once, but not as useful when you want to utilize the computational power of all the cpu cores in your laptop, as the Global Interpreter Lock will prevent you to do so. (This was worked around by the multiprocessing module, but that's another story)

那么,为什么Python的人们需要协同程序?主要的答案是反应式编程得到了真正时下的流行。
当然还有其他方面,如不想启动一个新的线程,你尽一切宁静查询(部分
Python库被称为泄漏线程ID,直到他们最终崩溃),或只是想摆脱一切
不必要的多线程原语像互斥和信号量。 (我指的是原语的话可以省略的
code可以改写为协同程序。当然,当你的他们需要真正的的多线程。)这就是为什么期货
开发的。

So, why do Python people need coroutines? The main answer is that reactive programming got really popular nowadays. There are of course other aspects, like not wanting to start a new thread for every restful query you make, (some Python libraries are known to leak thread ids until they eventually crash) or just wanting to get rid of all the unnecessary multithreading primitives like mutexes and semaphores. (I mean those primitives can be omitted if your code can be rewritten to coroutines. Sure they are needed when you do real multithreading.) And that is why futures were developed.

Python的期货不允许任何形式的链接。它们不打算使用的方式。请记住,JavaScript的
承诺人对传销改为漂亮链方案,所以平仓是必要的。 但是自动
平仓需要的具体code写入,需要未来的分辨率,使其提供的区别
由它们的类型或属性的值。也就是说,这将是更复杂(===难以调试),并且将一个
对弱类型小步,这是对蟒蛇的主要原则。 Python的期货是轻量级的,
干净,易于理解。他们只是不需要自动平仓。

Python's futures do not allow chaining in any form. They are not intended to be used that way. Remember, JavaScript's promises were to change the pyramid scheme to a nice chain scheme, so unwinding was necessary. But automatic unwinding needs specific code to be written, and needs the future resolution to make distinction between supplied values by their types or properties. That is, it would be more complex (=== harder to debug), and would be a little step towards weak typing, which is against the main principles of python. Python's futures are lightweight, clean and easy to understand. They just don't need automatic unwinding.

这篇关于在ES6承诺与PEP3148期货链差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆