为什么下面的代码有效?我认为应该有编译时错误。我认为身体中的return
返回一个int
(值1
)。如果不是,它必须返回一个Future
,这也不符合返回类型。await
怎么了?
void longRun() async {
return await Future.delayed(
Duration(seconds: 3),
()=>1,
);
}
如果我将await
部分分配给变量,如下所示,编译器开始抱怨,这是显而易见且易于理解的。但是为什么上面的代码不抱怨呢?
void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}
继续学习我发现了更令人困惑的事情:以下所有版本都有效。它们看起来是相同的,有线的。
版本1:
void longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
版本2:
Future<void> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
版本3:
Future<int> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
版本4:
Future longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
版本5:
Future longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
版本6:
void longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
这主要是关于=>
的行为,而不是关于await
。 通常() => 1
可以被认为是() { return 1; }
的简写,但这实际上过于简单化了。
为了方便起见,void
函数允许使用=>
,以便人们可以编写以下内容:
bool functionWithSideEffect() {
print('Some side effect');
return true;
}
void foo() => functionWithSideEffect();
int _someValue = 0;
set someValue(int value) => _someValue = value;
即使这些是不合法的:
var foo() {
// error: A value of type 'bool' can't be returned from the function 'foo'
// because it has a return type of 'void'.
return functionWithSideEffect();
}
set someValue(int value) {
// error: A value of type 'int' can't be returned from the function
// 'someValue' because it has a return type of 'void'.
return _someValue = value;
}
你困惑的症结在于,() => 1
可以是int Function()
的,也可以是void Function()
的,类型推断在给定不同的约束下选择不同的东西。
当您执行以下操作时:
然后void longRun() async { return await Future.delayed( Duration(seconds: 3), ()=>1, ); }
类型推断由外向内工作,通过不受约束的返回表达式传播longRun
的void
返回类型。Future.delayed
调用推断为Future<void>.delayed
,() => 1
回调推断为void Function()
。(可以说,返回Future<void>
的void
async
函数可能被视为错误。
相反,当您执行以下操作时:
void longRun() async { var foo = await Future.delayed( Duration(seconds: 3), ()=>1, ); return foo; }
现在类型推断以另一个方向(由内而外)运行:foo
没有显式类型,因此它不会约束右侧。因此() => 1
被假定为int Function()
,这导致Future.delayed
被推断为Future<int>.delayed
,foo
被推断为int
。 由于foo
是一个int
,试图从声明返回void
的函数返回它是一个错误。
版本2:
Future<void> longRun() async { return await Future.delayed(Duration(seconds:2), ()=>1); }
您已将longRun
的返回类型从void
更改为Future<void>
。async
函数通常应该返回一个Future
但也可能返回void
即发即弃。 与版本 1 的唯一区别是,调用方现在可以在longRun
完成时等待(触发回调)。() => 1
仍然被推断为void Function()
。
版本3:
Future<int> longRun() async { return await Future.delayed(Duration(seconds:2), ()=>1); }
与版本 2 相同,只是longRun
返回Future<int>
而不是Future<void>
。 现在() => 1
被推断为int Function()
。
版本4:
Future longRun() async { return await Future.delayed(Duration(seconds:2), ()=>1); }
longRun
的返回类型现在是Future
,这意味着Future<dynamic>
。() => 1
被推断为dynamic Function()
。
版本5:
Future longRun() async { await Future.delayed(Duration(seconds:2), ()=>1); }
与版本 4 相同,只是没有明确的return
语句。 现在Future.delayed
表达式不再受longRun
返回类型的约束,推理将由内而外流动; 假定() => 1
int Function()
。
版本6:
void longRun() async { await Future.delayed(Duration(seconds:2), ()=>1); }
与版本 5 相同,只是longRun
返回void
并且是一个即发即弃函数。longRun
完成时无法通知呼叫者。
(注意,在上面,当我写() => 1
时被推断为int Function()
或void Function()
,它确实应该是FutureOr<int> Function()
或FutureOr<void> Function()
,但这不是重要的部分。
先发制人地解决一些潜在的后续问题:
为什么是:
void longRun() async {
int f() {
return 1;
}
return await Future<void>.delayed(
Duration(seconds: 3),
f,
);
}
好的,但是:
void longRun() async {
return await Future<void>.delayed(
Duration(seconds: 3),
// info: Don't assign to void.
() {
return 1;
},
);
}
产生分析投诉? 这两个版本都是合法的,因为当U
是T
的子类型时,用Future<U>
代替Future<T>
是合法的。 当T
void
时,U
可以是任何东西;该值可以忽略。(在 Dart 中,部分出于历史原因,当它并不总是具有void
类型时,void
实际上意味着该值不能使用,而不是没有值。void x = 42;
是合法的,但尝试读取x
的值将是一个错误。 这就是"不分配给无效"分析投诉未归类为错误的原因。
在使用匿名函数的第二个版本中,return
语句的更紧密位置允许分析器执行更广泛的检查。