await引入dart中的程序有什么后果?

What the consequence does await introduce the program in dart?

为什么下面的代码有效?我认为应该有编译时错误。我认为正文 return 中的 returnint(值 1)。如果不是,它一定是 returning 一个 Future,它也不符合 return 类型。 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; } 的 shorthand,但这实际上过于简单化了。

=> 被允许用于 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,
 );
}

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

相比之下,当您这样做时:

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

现在类型推断在 other 方向(由内而外)运行:foo 没有显式类型,因此它不约束右侧.因此 () => 1 被假定为 int Function(),这导致 Future.delayed 被推断为 Future<int>.delayed 并且 foo 被推断为 int .由于 foo 是一个 int,因此尝试从声明为 return void 的函数 return 它是错误的。

version2:

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

您已将 longRun 的 return 类型从 void 更改为 Future<void>。与版本 1 的唯一区别是调用者现在可以在 longRun 完成时等待(触发回调)。 () => 1 仍被推断为 void Function()

version3:

Future<int> longRun() async {
   return await Future.delayed(Duration(seconds:2), ()=>1);
}

longRun returns Future<int> 而不是 Future<void> 外,与版本 2 相同。现在 () => 1 被推断为 int Function().

version4:

Future longRun() async {
   return await Future.delayed(Duration(seconds:2), ()=>1);
}

longRun 的 return 类型现在是 Future,这意味着 Future<dynamic>() => 1 被推断为 dynamic Function()

version5:

Future longRun() async {
   await Future.delayed(Duration(seconds:2), ()=>1);
}

除了没有明确的 return 声明外,与版本 4 相同。现在 Future.delayed 表达式不再受限于 longRun 的 return 类型,推理将由内而外; () => 1 假定为 int Function()

version6:

void longRun() async {
   await Future.delayed(Duration(seconds:2), ()=>1);
}

除了 longRun returns void 并且是一个即发即弃函数外,与版本 5 相同。 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 的值将是一个错误。这就是为什么“不要分配给 void”分析投诉未归类为错误的原因。)

在使用匿名函数的第二个版本中,return 语句的更严格的局部性允许分析器执行更广泛的检查。