等待在飞镖中引入程序的后果是什么?



为什么下面的代码有效?我认为应该有编译时错误。我认为身体中的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,
);
}

类型推断由外向内工作,通过不受约束的返回表达式传播longRunvoid返回类型。Future.delayed调用推断为Future<void>.delayed() => 1回调推断为void Function()。(可以说,返回Future<void>voidasync函数可能被视为错误。

相反,当您执行以下操作时:

void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}

现在类型推断以另一个方向(由内而外)运行:foo没有显式类型,因此它不会约束右侧。因此() => 1被假定为int Function(),这导致Future.delayed被推断为Future<int>.delayedfoo被推断为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返回类型的约束,推理将由内而外流动; 假定() => 1int 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;
},
);
}

产生分析投诉? 这两个版本都是合法的,因为当UT的子类型时,用Future<U>代替Future<T>是合法的。 当Tvoid时,U可以是任何东西;该值可以忽略。(在 Dart 中,部分出于历史原因,当它并不总是具有void类型时,void实际上意味着该值不能使用,而不是没有值。void x = 42;是合法的,但尝试读取x的值将是一个错误。 这就是"不分配给无效"分析投诉未归类为错误的原因。

在使用匿名函数的第二个版本中,return语句的更紧密位置允许分析器执行更广泛的检查。

最新更新