ES2017 - 异步与收益
ES2017 - Async vs. Yield
我对当前关于将异步函数和关键字 await
添加到下一个 EcmaScript 的讨论感到困惑。
我不明白为什么在function
关键字之前必须有async
关键字。
从我的角度来看,await
关键字等待生成器或承诺 完成 的结果,一个函数的 return
应该足够了。
await
应该在正常函数和生成器函数中简单可用,无需额外的 async
标记。
如果我需要创建一个函数,作为 await
的结果,我只需使用一个 promise。
我问的原因是this很好的解释,下面的例子来自:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
也可以像普通函数一样完成,如果一个函数的执行会等待完成洞函数,直到所有等待都完成。
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
在我看来,整个函数的执行一直持续到下一个滴答(等待实现)完成。与 Generator-Function 的区别在于 next() 触发并更改对象的值和完成字段。相反,函数将在完成时简单地返回结果,触发器是一个函数内部触发器,如 while 循环。
I do not understand why it is necessary to have the async
keyword before the function keyword.
出于与我们在生成器函数之前有 *
符号相同的原因:它们将函数标记为非凡的。它们在这方面非常相似 - 它们添加了一个视觉标记,表明此函数的主体本身不会 运行 完成,但可以与其他代码任意交错。
*
表示一个生成器函数,它将始终 return 一个生成器,可以通过类似于迭代器的方式从外部使用它来推进(和停止)。
async
表示一个异步函数,它总是 return 一个依赖于其他承诺的承诺,并且其执行与其他异步操作并发(并且可能从外部取消)。
的确,关键字不是绝对必要的,函数的类型可以根据相应的关键字(yield(*)
/await
)是否出现在其主体中来确定,但这会导致难以维护的代码:
- 不太好理解,因为需要扫描全身才能确定种类
- 更容易出错,因为很容易通过 adding/removing 那些关键字破坏函数而不会出现语法错误
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
听起来你想要一个阻塞函数,它是 very bad idea 在并发设置中。
前面的 async 关键字的原因很简单,所以你知道 return 值将被转换为一个承诺。如果没有关键字,解释器怎么知道要这样做。
我认为这是在 C# 中首次引入的,而 EcmaScript 正在从 TypeScript 中抢走一些东西。 TypeScript 和 C# 是由 Anders Hejlsberg 构想的,有相似之处。
假设您有一个函数(这个函数只是为了进行一些异步工作)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
这个函数会让我们等待一个随机时间和return一个Promise(如果你使用jQueryPromise类似于Deferred)对象。今天要使用这个函数,你会写这样的东西
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
这很好。现在让我们尝试 return 日志消息
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
好的,这还不错,但是代码中有两个 return 语句和一个函数。
现在有了异步,这看起来像这样
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
代码简短且内联。如果你写了很多 .then() 或 . done() 你知道代码有多难读。
现在为什么要在函数前面加上 async 关键字。嗯,这表明您的 return 值不是 returned 的值。理论上你可以写这个(这可以在c#中完成我不知道js是否允许,因为它没有完成)。
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
你看,你 return 一个数字,但实际 return 将是一个你不必使用的 Promise
return new Promise(function(resolve, reject) { resolve(5);};
因为你不能等待一个数字,只有 Promise await test(false)
会抛出异常,而 await test(true)
如果你不在前面指示异步则不会。
通过将函数标记为 async
,您告诉 JS 始终 return Promise。
因为它总是 return 一个 Promise,它也可以 await 在它自己的块内的 promise。把它想象成一个巨大的 Promise 链——函数内部发生的事情被有效地固定到它的内部 .then()
块上,returned 是链中最后的 .then()
。
例如,这个函数...
async function test() {
return 'hello world';
}
... return是一个承诺。所以你可以像一个一样执行它,.then()
和所有。
test().then(message => {
// message = 'hello world'
});
所以...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
大致类似于...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
在这两种情况下,传递给 test().then()
的回调将接收 report
作为其第一个参数。
生成器(即标记函数 *
并使用 yield
关键字)完全是一个不同的概念。他们不使用承诺。它们有效地允许您在代码的不同部分之间 'jump',从函数内部产生结果,然后跳回到该点并继续下一个 yield 块。
虽然它们感觉有些相似(即 'halting' 执行直到其他地方发生某些事情),async/await
只会给你那种错觉,因为它 与 Promise 执行的内部顺序混淆。这不是 实际上 等待 - 它只是在回调发生时洗牌。
相比之下,生成器的实现方式不同,因此生成器可以保持状态并进行迭代。同样,与 Promises 无关。
这条线更加模糊,因为在撰写本文时,对 async/await 的支持还很可怕; Chakracore 原生支持它,V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await
and convert the code to generators。因此得出生成器和 async/await 相同的结论是错误的;他们不是......碰巧你可以混杂 yield
如何与 Promises 一起工作以获得类似的结果。
更新:2017 年 11 月
Node LTS 现在具有原生 async/await
支持,因此您永远不需要使用生成器来模拟 Promises。
这些答案都给出了为什么 async 关键字是一件好事的有效论据,但其中 none 实际上提到了 必须 添加它的真正原因符合规范。
原因是这是一个有效的 ES7 之前的 JS
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
按照你的逻辑,foo()
return Promise{42}
还是 "awaiting 42"
? (return使用 Promise 会破坏向后兼容性)
所以答案是:await
是一个常规标识符,它只被视为异步函数中的关键字,因此必须以某种方式对其进行标记。
有趣的事实:原始规范提出了更轻量级的异步语法 function^ foo() {}
。
我对当前关于将异步函数和关键字 await
添加到下一个 EcmaScript 的讨论感到困惑。
我不明白为什么在function
关键字之前必须有async
关键字。
从我的角度来看,await
关键字等待生成器或承诺 完成 的结果,一个函数的 return
应该足够了。
await
应该在正常函数和生成器函数中简单可用,无需额外的 async
标记。
如果我需要创建一个函数,作为 await
的结果,我只需使用一个 promise。
我问的原因是this很好的解释,下面的例子来自:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
也可以像普通函数一样完成,如果一个函数的执行会等待完成洞函数,直到所有等待都完成。
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
在我看来,整个函数的执行一直持续到下一个滴答(等待实现)完成。与 Generator-Function 的区别在于 next() 触发并更改对象的值和完成字段。相反,函数将在完成时简单地返回结果,触发器是一个函数内部触发器,如 while 循环。
I do not understand why it is necessary to have the
async
keyword before the function keyword.
出于与我们在生成器函数之前有 *
符号相同的原因:它们将函数标记为非凡的。它们在这方面非常相似 - 它们添加了一个视觉标记,表明此函数的主体本身不会 运行 完成,但可以与其他代码任意交错。
*
表示一个生成器函数,它将始终 return 一个生成器,可以通过类似于迭代器的方式从外部使用它来推进(和停止)。async
表示一个异步函数,它总是 return 一个依赖于其他承诺的承诺,并且其执行与其他异步操作并发(并且可能从外部取消)。
的确,关键字不是绝对必要的,函数的类型可以根据相应的关键字(yield(*)
/await
)是否出现在其主体中来确定,但这会导致难以维护的代码:
- 不太好理解,因为需要扫描全身才能确定种类
- 更容易出错,因为很容易通过 adding/removing 那些关键字破坏函数而不会出现语法错误
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
听起来你想要一个阻塞函数,它是 very bad idea 在并发设置中。
前面的 async 关键字的原因很简单,所以你知道 return 值将被转换为一个承诺。如果没有关键字,解释器怎么知道要这样做。 我认为这是在 C# 中首次引入的,而 EcmaScript 正在从 TypeScript 中抢走一些东西。 TypeScript 和 C# 是由 Anders Hejlsberg 构想的,有相似之处。 假设您有一个函数(这个函数只是为了进行一些异步工作)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
这个函数会让我们等待一个随机时间和return一个Promise(如果你使用jQueryPromise类似于Deferred)对象。今天要使用这个函数,你会写这样的东西
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
这很好。现在让我们尝试 return 日志消息
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
好的,这还不错,但是代码中有两个 return 语句和一个函数。
现在有了异步,这看起来像这样
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
代码简短且内联。如果你写了很多 .then() 或 . done() 你知道代码有多难读。
现在为什么要在函数前面加上 async 关键字。嗯,这表明您的 return 值不是 returned 的值。理论上你可以写这个(这可以在c#中完成我不知道js是否允许,因为它没有完成)。
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
你看,你 return 一个数字,但实际 return 将是一个你不必使用的 Promise
return new Promise(function(resolve, reject) { resolve(5);};
因为你不能等待一个数字,只有 Promise await test(false)
会抛出异常,而 await test(true)
如果你不在前面指示异步则不会。
通过将函数标记为 async
,您告诉 JS 始终 return Promise。
因为它总是 return 一个 Promise,它也可以 await 在它自己的块内的 promise。把它想象成一个巨大的 Promise 链——函数内部发生的事情被有效地固定到它的内部 .then()
块上,returned 是链中最后的 .then()
。
例如,这个函数...
async function test() {
return 'hello world';
}
... return是一个承诺。所以你可以像一个一样执行它,.then()
和所有。
test().then(message => {
// message = 'hello world'
});
所以...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
大致类似于...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
在这两种情况下,传递给 test().then()
的回调将接收 report
作为其第一个参数。
生成器(即标记函数 *
并使用 yield
关键字)完全是一个不同的概念。他们不使用承诺。它们有效地允许您在代码的不同部分之间 'jump',从函数内部产生结果,然后跳回到该点并继续下一个 yield 块。
虽然它们感觉有些相似(即 'halting' 执行直到其他地方发生某些事情),async/await
只会给你那种错觉,因为它 与 Promise 执行的内部顺序混淆。这不是 实际上 等待 - 它只是在回调发生时洗牌。
相比之下,生成器的实现方式不同,因此生成器可以保持状态并进行迭代。同样,与 Promises 无关。
这条线更加模糊,因为在撰写本文时,对 async/await 的支持还很可怕; Chakracore 原生支持它,V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await
and convert the code to generators。因此得出生成器和 async/await 相同的结论是错误的;他们不是......碰巧你可以混杂 yield
如何与 Promises 一起工作以获得类似的结果。
更新:2017 年 11 月
Node LTS 现在具有原生 async/await
支持,因此您永远不需要使用生成器来模拟 Promises。
这些答案都给出了为什么 async 关键字是一件好事的有效论据,但其中 none 实际上提到了 必须 添加它的真正原因符合规范。
原因是这是一个有效的 ES7 之前的 JS
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
按照你的逻辑,foo()
return Promise{42}
还是 "awaiting 42"
? (return使用 Promise 会破坏向后兼容性)
所以答案是:await
是一个常规标识符,它只被视为异步函数中的关键字,因此必须以某种方式对其进行标记。
有趣的事实:原始规范提出了更轻量级的异步语法 function^ foo() {}
。