问题描述
我对当前关于添加异步函数和关键字await
下一个EcmaScript的讨论感到困惑。
我不明白为什么有必要在function
关键字之前使用async
关键字。
从我的观点来看, await
关键字等待做了发电机或承诺的结果,函数的return
应该是足够的。
await
应该可以在普通函数和生成器函数中使用,而不需要额外的async
标记。
如果我需要创建一个函数作为await
的结果,我只需使用一个promise。
我的理由是很好的解释,下面的例子来自:
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循环。
1楼
我不明白为什么有必要在function关键字之前使用
async
关键字。
出于同样的原因,我们在生成器函数之前有*
符号:它们将函数标记为非凡。
它们在这方面非常相似 - 它们添加了一个可视标记,该函数的主体本身不会完成运行,但可以与其他代码任意交错。
-
*
表示一个生成器函数,它总是返回一个可以从外部进行推进(和停止)的生成器,类似于迭代器。 -
async
表示一个异步函数,它将始终返回一个依赖于其他promises的promise,其执行与其他异步操作并发(并且可能从外部取消)。
确实,关键字不是绝对必要的,函数的类型可以通过相应的关键字( yield(*)
/ await
)是否出现在其正文中来确定,但这会导致代码的可维护性降低:
- 不易理解,因为你需要扫描整个身体来确定那种
- 更多的errorprone,因为通过添加/删除这些关键字而不会出现语法错误很容易破坏功能
一个正常的函数,执行将等待完成孔体直到满足所有等待
这听起来像你想要一个阻塞功能,这在并发设置中是一个 。
2楼
通过将函数标记为async
,您告诉JS始终返回Promise。
因为它总会返回一个Promise,它也可以等待它自己的块内的promise。
想象它就像一个巨大的Promise链 - 函数内部发生的事情有效地被固定到它的内部.then()
块,返回的是链中的最终.then()
。
例如,这个功能......
async function test() {
return 'hello world';
}
...返回一个承诺。
所以你可以像一个, .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
关键字)是完全不同的概念。
他们不使用Promises。
它们有效地允许您在代码的不同部分之间“跳转”,从函数内部生成结果,然后跳回到该点并恢复到下一个yield块。
虽然他们觉得有些相似(即'暂停'执行,直到某些事情发生在其他地方), async/await
只会给你这种错觉,因为它与Promise执行的内部顺序混淆 。
它实际上并没有等待 - 当回调发生时它只是在洗牌。
相反,生成器的实现方式不同,因此生成器可以维护状态并进行迭代。 再一次,与Promises无关。
这条线路进一步模糊,因为在目前的写作时,对async / await的支持很吓人;
Chakracore原生支持它, 。
与此同时,像Babel这样的转换允许你编写async/await
并将代码转换为 。
结论发电机和异步/等待是相同的是错误的;
它们不是......只是碰巧你可以将yield
与Promises一起工作以获得类似的结果。
更新:2017年11月
节点LTS现在具有本机async/await
支持,因此您永远不需要使用生成器来模拟Promises。
3楼
这些答案都给出了为什么async关键字是好事的有效论据,但它们都没有提到为什么必须将它添加到规范中的真正原因。
原因是这是一个有效的JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
根据你的逻辑, foo()
返回Promise{42}
还是"awaiting 42"
?
(返回Promise会破坏向后兼容性)
所以答案是: await
是一个常规标识符,它只被视为异步函数中的关键字,因此必须以某种方式标记它们。
有趣的事实:原始规范为异步语法提出了更轻量级的function^ foo() {}
。
4楼
前面的async关键字的原因很简单,因此您知道返回值将转换为promise。 如果没有关键字,解释器将如何知道这样做。 我认为这是首次在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);
}));
}
这个函数会让我们等待一个随机时间并返回一个Promise(如果你使用jQuery Promise类似于Deferred)对象。 今天要使用这个功能你会写这样的东西
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
这很好。 现在让我们尝试返回日志消息
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关键字在函数前面。 那么这是为了表明你的返回值不是返回的值。 理论上你可以写这个(这可以在c#中完成我不知道js是否允许,因为它没有完成)。
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
你看,你返回一个数字,但实际的回报将是一个你不必使用的return new Promise(function(resolve, reject) { resolve(5);};
因为你不能等待一个数字只有一个Promise await test(false)
将抛出异常,如果你没有在前面指出异步,则await test(true)
。