Generators

2018-10-11 11:17 更新

Generators(发电机)提供了一种更紧凑的写入迭代程序的方法。发电机通过在发电机和调用代码之间来回传递控制来工作。为了提供迭代的值,代替返回一次或者需要像数组那样可以记忆密集的东西,发电机可以根据需要多次产生调用代码的值。

Generators可以做Async功能; Async Generators的行为与普通Generators类似,除了每个产生的值都是Awaitable就是await这样。

Async Generators

要从Async Generators获取值或键/值对,您可以分别返回HH \ AsyncIteratorHH \ AsyncKeyedIterator

以下是使用Async实用功能的示例 usleep()模仿一个秒针倒计时钟。请注意,在happy_new_year() foreach循环中我们有语法await as。这是调用的简写await $ait->next()。

<?hh

namespace Hack\UserDocumentation\Async\Generators\Examples\Iterate;

const SECOND = 1000000; // microseconds

async function countdown(int $from): AsyncIterator<int> {
  for ($i = $from; $i >= 0; --$i) {
    await \HH\Asio\usleep(SECOND);
    // Every second, a value will be yielded back to the caller,
    // happy_new_year()
    yield $i;
  }
}

async function happy_new_year(int $start): Awaitable<void> {
  // Get the AsyncIterator that enables the countdown
  $ait = countdown($start);
  foreach ($ait await as $time) {
    // we are awaiting the returned awaitable, so this will be an int
    if ($time > 0) {
      echo $time . "\n";
    } else {
      echo "HAPPY NEW YEAR!!!\n";
    }
  }
}

function run(): void {
  \HH\Asio\join(happy_new_year(5)); // 5 second countdown
}

run();

Output

5
4
3
2
1
HAPPY NEW YEAR!!!

你必须使用await as; 否则你不会得到迭代值。

注意await as就像打电话一样await $gen->next(); 但是,await as如果可能,您应该始终使用。打电话给AsyncGenerator直接的方法很少需要。还要注意,在异步迭代器await as或调用next()实际上返回一个值(而不是void一般的迭代器)。

发送和提高

很少需要直接调用这些方法。await as应该是访问迭代程序返回值的最常见的用法。

您可以使用Generator发送值并在Generator上send()引发异常raise()

如果您正在做这两件事情,您的Generator必须返回AsyncGenerator。An AsyncGenenator有三种类型的参数。第一是关键。第二个是价值。第三种是传递给的类型send()

<?hh

namespace Hack\UserDocumentation\Async\Generators\Examples\Send;

const HALF_SECOND = 500000; // microseconds

async function get_name_string(int $id): Awaitable<string> {
  // simulate fetch to database where we would actually use $id
  await \HH\Asio\usleep(HALF_SECOND);
  return str_shuffle("ABCDEFG");
}

async function generate(): AsyncGenerator<int, string, int> {
  $id = yield 0 => ''; // initialize $id
  // $id is a ?int; you can pass null to send()
  while ($id !== null) {
    $name = await get_name_string($id);
    $id = yield $id => $name; // key/string pair
  }
}

async function associate_ids_to_names(
  Vector<int> $ids
): Awaitable<void> {
  $async_generator = generate();
  // You have to call next() before you send. So this is the priming step and
  // you will get the initialization result from generate()
  $result = await $async_generator->next();
  var_dump($result);

  foreach ($ids as $id) {
    // $result will be an array of ?int and string
    $result = await $async_generator->send($id);
    var_dump($result);
  }
}

function run(): void {
  $ids = Vector {1, 2, 3, 4};
  \HH\Asio\join(associate_ids_to_names($ids));
}

run();

Output

array(2) {
  [0]=>
  int(0)
  [1]=>
  string(0) ""
}
array(2) {
  [0]=>
  int(1)
  [1]=>
  string(7) "GEFDBAC"
}
array(2) {
  [0]=>
  int(2)
  [1]=>
  string(7) "FBGEACD"
}
array(2) {
  [0]=>
  int(3)
  [1]=>
  string(7) "GBCDFAE"
}
array(2) {
  [0]=>
  int(4)
  [1]=>
  string(7) "FCBGEAD"
}

这是如何将异常引发到Async Generators。

<?hh

namespace Hack\UserDocumentation\Async\Generators\Examples\Raise;

const HALF_SECOND = 500000; // microseconds

async function get_name_string(int $id): Awaitable<string> {
  // simulate fetch to database where we would actually use $id
  await \HH\Asio\usleep(HALF_SECOND);
  return str_shuffle("ABCDEFG");
}

async function generate(): AsyncGenerator<int, string, int> {
  $id = yield 0 => ''; // initialize $id
  // $id is a ?int; you can pass null to send()
  while ($id !== null) {
    $name = "";
    try {
      $name = await get_name_string($id);
      $id = yield $id => $name; // key/string pair
    } catch (\Exception $ex) {
      var_dump($ex->getMessage());
      $id = yield 0 => '';
    }
  }
}

async function associate_ids_to_names(
  Vector<int> $ids
): Awaitable<void> {
  $async_generator = generate();
  // You have to call next() before you send. So this is the priming step and
  // you will get the initialization result from generate()
  $result = await $async_generator->next();
  var_dump($result);

  foreach ($ids as $id) {
    if ($id === 3) {
      $result = await $async_generator->raise(
        new \Exception("Id of 3 is bad!")
      );
    } else {
      $result = await $async_generator->send($id);
    }
    var_dump($result);
  }
}

function run(): void {
  $ids = Vector {1, 2, 3, 4};
  \HH\Asio\join(associate_ids_to_names($ids));
}

run();

Output

array(2) {
  [0]=>
  int(0)
  [1]=>
  string(0) ""
}
array(2) {
  [0]=>
  int(1)
  [1]=>
  string(7) "GEAFBCD"
}
array(2) {
  [0]=>
  int(2)
  [1]=>
  string(7) "GFEDBAC"
}
string(15) "Id of 3 is bad!"
array(2) {
  [0]=>
  int(0)
  [1]=>
  string(0) ""
}
array(2) {
  [0]=>
  int(4)
  [1]=>
  string(7) "FGCAEBD"
}
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号