tor*_*gen 7 javascript python future promise async-await
我对ES6 Promises和PEP3148期货的实施差异进行推理有点困惑.在Javascript中,当Promise与另一个Promise一起解决时,"outer"promise会在解决或拒绝后继承"内部"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())
Run Code Online (Sandbox Code Playgroud)
在Javascript中,完全等效的代码是这样的:
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());
})();
Run Code Online (Sandbox Code Playgroud)
sleep 为完整起见而提供的功能.
42正如预期的那样打印Javascript代码.Python代码打印<coroutine object foo at 0x102a05678>和关于"coroutine'foo'的投诉从未等待过".
通过这种方式,JS允许您选择控制将在当前执行上下文中消失的时间点,立即执行awaitpromises,或让调用者等待它们.Python总是让你没有其他选择,而不是总是awaitFuture/coroutine,因为否则你将不得不用一个丑陋的包装器函数在循环中展开Future链,如下所示:
async def unwind(value):
while hasattr(value, '__await__'):
value = await value
return value
Run Code Online (Sandbox Code Playgroud)
所以,问题是:这个决定背后有什么理由吗?为什么Python不允许链式期货?有关于它的讨论吗?是否可以采取任何措施使行为更接近Promise?
让我展示一下JavaScript的Promises和Python的未来的快速比较,我可以在其中指出主要的用例并揭示决策背后的原因.
我将使用以下虚拟示例来演示异步函数的使用:
async function concatNamesById(id1, id2) {
return (await getNameById(id1)) + ', ' + (await getNameById(id2));
}
Run Code Online (Sandbox Code Playgroud)
回到白天,在Promises的概念出现之前,人们使用回调编写了他们的代码.关于哪个参数应该是回调,如何处理错误等等仍然存在各种约定......最后,我们的函数看起来像这样:
// 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);
}
});
}
});
}
Run Code Online (Sandbox Code Playgroud)
这与示例相同,是的,我有意使用4个缩进空间来放大所谓的回调地狱或 厄运金字塔的问题.使用JavaScript的人们多年来都在编写这样的代码!
然后,Kris Kowal带着他火红的Q库,通过引入Promises的概念来拯救失望的JavaScript社区.该名称故意不是"未来"或"任务".Promise概念的主要目标是摆脱金字塔.为了实现这个承诺,有一种then方法,它不仅允许您订阅在获得承诺值时触发的事件,而且还将返回另一个承诺,允许链接.这就是使Promises和future成为一个不同概念的原因.承诺更多一点.
// 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
}
Run Code Online (Sandbox Code Playgroud)
看到?必须从then回调中解包返回的promises,以构建一个干净,透明的链.(我自己写了这种异步代码超过一年.)然而,当你需要像条件分支或循环这样的控制流程时,事情会变得复杂.当ES6的第一个编译器/转换器(如6to5)出现时,人们慢慢开始使用发电机.ES6生成器是双向的,这意味着生成器不仅生成值,而且可以在每次迭代时接收提供的值.这允许我们编写以下代码:
// using generators and promises
const concatNamesById = Q.async(function*(id1, id2) {
return (yield getNameById(id1)) + ', ' + (yield getNameById(id2));
});
Run Code Online (Sandbox Code Playgroud)
仍在使用promises,Q.async从生成器生成异步函数.那里没有黑魔法,这个包装函数只使用promise.then(或多或少)实现.我们快到了.
今天,由于async-await的ES7规范已经相当成熟,任何人都可以使用BabelJS将异步ES7代码编译为ES5 .
// using real async-await
async function concatNamesById(id1, id2) {
return (await getNameById(id1)) + ', ' + (await getNameById(id2));
}
Run Code Online (Sandbox Code Playgroud)
这样可行:
async foo() {
return /* await */ sleep('bar', 1000);
// No await is needed!
}
Run Code Online (Sandbox Code Playgroud)
这样做:
async foo() {
return await await await 'bar';
// You can write await pretty much everywhere you want!
}
Run Code Online (Sandbox Code Playgroud)
这种弱/动态/鸭子打字非常适合JavaScript的世界观.
你是对的,你可以在没有等待的情况下从异步函数返回一个promise,它就会被解除.这不是一个真正的决定,但它是如何promise.then运作的直接结果,因为它解除了使链条变得舒适的承诺.尽管如此,我认为在每次异步调用之前编写等待是一个很好的做法,以明确你知道调用是异步的.由于缺少等待关键字,我们每天都有多个错误,因为它们不会导致瞬时错误,只是一堆并行运行的随机任务.我喜欢调试它们.认真.
让我们看看Python人员在异步之前做了什么 - 等待在python中引入了coroutines:
def concatNamesById(id1, id2):
return getNameById(id1) + ', ' + getNameById(id2);
Run Code Online (Sandbox Code Playgroud)
等什么?期货在哪里?回调金字塔在哪里?重点是Python人员没有JavaScript人员遇到的任何问题.他们只是使用阻止调用.
那么为什么JavaScript人们不使用阻止调用?因为他们不能!好吧,他们想要.相信我.在他们介绍WebWorkers之前,所有JavaScript代码都在gui线程上运行,并且任何阻塞调用都会导致ui冻结!这是不可取的,因此编写规范的人会尽一切努力来防止这些事情发生.截至今天,我知道在浏览器中阻止UI线程的唯一方法:
async = false选项一起使用目前你无法在JavaScript中实现自旋锁和类似的东西,没有办法.(直到浏览器供应商开始实施共享数组缓冲区之类的东西,我担心,一旦发烧友业余爱好者开始使用它们,我们就会带来一种特殊的地狱般的痛苦)
另一方面,在Python中阻塞调用没有任何问题,因为通常没有'gui线程'这样的东西.如果你仍然需要一些并行性,你可以开始一个新的线程,并继续努力.当您想要一次运行多个SOAP请求时,这非常有用,但是当您想要利用笔记本电脑中所有CPU内核的计算能力时,这一点非常有用,因为Global Interpreter Lock会阻止您这样做.(这是由多处理模块解决的,但这是另一个故事)
那么,为什么Python人需要协程?主要答案是反应式编程现在非常流行.当然还有其他方面,比如不想为你做的每一个宁静的查询启动一个新线程(一些Python库已经泄漏线程id直到它们最终崩溃)或者只是想要摆脱所有不必要的多线程原语互斥和信号量.(我的意思是如果您的代码可以重写为协同程序,那么这些原语可以省略.当您进行真正的多线程时,确实需要它们.)这就是为什么期货被开发出来的原因.
Python的未来不允许任何形式的链接.它们不打算以这种方式使用.请记住,JavaScript的承诺是将金字塔方案改为一个很好的链式方案,因此必须展开.但是自动展开需要编写特定的代码,并且需要将来的解决方案来区分它们的类型或属性所提供的值.也就是说,它会更复杂(===更难调试),并且会向弱打字迈出一小步,这违背了python的主要原则.Python的未来是轻量级,干净且易于理解的.他们只是不需要自动展开.
| 归档时间: |
|
| 查看次数: |
532 次 |
| 最近记录: |