在Javascript中,您需要明确地告诉代码以等待变量解析,然后再将其与await
关键字一起使用,如下所示:
let x = await doSomethingAsynchronous()
console.log(`The result is ${x.result}`)
如果未特别包含“ await”,则Javascript将很高兴在解析x.result
之前尝试访问x
,从而引发错误。为什么这是默认设置?程序员多久要使用一个变量,然后再对其进行解析?为什么Javascript编译器没有在使用未解析的变量之前直接插入await
?
我对Javascript还是很陌生,所以如果答案很明显,对不起,并在此先感谢您的帮助。
脚注-
x.result
),而不仅仅是传递给另一个函数,在这种情况下,当然并不需要立即进行解析。await
关键字非常有用,以防程序员想要显式调用它,例如,当尚未使用要解析的变量,而是使用下一个行取决于doSomethingAsynchronous
的某些副作用。因此,我不是在问为什么语言语法中存在它,而是为什么默认情况下不插入它。答案 0 :(得分:1)
程序员在解析变量之前多久要使用一次变量?
实际上很少。我只记得有一种情况我评论等待,就像这样:
const result = await database();
if(check)
/*await*/ notifyUser();
return result;
在这种情况下,我需要它,因为我不介意在返回响应后通知是否到达。而且我仍然在这里留下评论,以确保将来的读者获得异步。因此,是的,如果有相反的关键字,则我(和其他人)将节省很多键入操作。
[然后]为什么这个[不等待]默认?
我只能看到两个原因:
如果等待是隐式的,则无法从代码中得知其行为,函数调用可能导致函数执行停止,并且您得到意外的执行顺序(难以调试=错误):
async function stuff() {
a();
console.log("a");
b();
console.log("b");
}
stuff(); stuff(); // a, a, b, b ... what the ... ?
使用await
是明确的,您知道async function
可能会停在哪里。
此外,引擎还必须检查每个函数调用和其上的每个方法调用的响应,以返回承诺:
promise() // await this?
.then(/*...*/); // or this?
maybe(); // or this?
由于JavaScript是一种非常动态的语言,通常返回一个数字的函数可能会突然返回一个Promise,因此引擎将不得不检查每个调用以获取一个Promise,这会带来很多开销,并且很难做到这一点。优化。使用await
,引擎不必检查,它只会在您告诉它等待的地方等待,这可以提供更好的优化。
但是我不属于ES Comitee,要想得到明确的答案,请问他们,我只能推测一下他们的原因。
答案 1 :(得分:0)
我能想到的主要原因有两个:
2)doSomethingAsynchronous()
返回一个Promise,并且想要对那个Promise进行除await
之外的其他事情是完全有效和普遍的。
这里是一个例子:
const promises = [];
for ( let i = 0; i < 100; i++ )
promises.push( doSomethingAsynchronous( ) );
const results = await Promise.all( promises );
这将立即启动所有100个异步任务,直到最后才等待;与将await
放入循环中(在开始下一个任务之前会等待每个Promise)不同。
实际上,承诺的存在时间比异步/等待时间长,并且隐式等待这些承诺会破坏很多现有代码。由于JavaScript用于网络浏览器,因此它们在不破坏向后兼容性方面非常严格。
2)这将使读/写JavaScript更加困难。考虑以下代码:
setTimeout( function ( ) { console.log( 'FOO' ) }, 0 );
console.log( x.result );
由于JavaScript在事件循环继续之前就运行了代码,因此很明显console.log( x.result );
在console.log( 'FOO' );
之前先运行。如果JavaScript具有隐式await
,则您将不知道x.result
是否是同步的,这意味着这些日志的顺序将变得不确定。一般来说,知道您的代码将要运行的顺序非常重要,并且无法确定该顺序是否定义正确,将使用JavaScript编程非常困难。
这里是一个例子:
(async _ => {
const x = new Promise( resolve => setTimeout( _ => resolve( { foo: 'bar' } ), 1000 ) );
setTimeout( function ( ) { console.log( '2) async' ) }, 0 );
x.foo;
console.log( '1) Sync' );
})();
因为x.foo
是同步操作(访问Promise的foo
属性将是未定义的,但这只是为了演示,请耐心等待),日志的顺序是预期的,但是如果该代码中存在隐式等待(我们可以使用(await x)
进行模拟:
(async _ => {
const x = new Promise( resolve => setTimeout( _ => resolve( { foo: 'bar' } ), 1000 ) );
setTimeout( function ( ) { console.log( '2) async' ) }, 0 );
(await x).foo; // Simulate implicit await
console.log( '1) Sync??' );
})();
日志以相反的顺序发生(或者对于较短的计时器,可能是不确定的顺序)。
您也无法仅通过查看就知道x.result
是否会引发错误。您将需要查看在x
的定义位置,以检查它是否是一个Promise(如果Promise被拒绝,等待Promise会引发错误),以知道该行可能会抛出。