Generators

2018-10-11 11:17 更新

Generators(發(fā)電機(jī))提供了一種更緊湊的寫入迭代程序的方法。發(fā)電機(jī)通過在發(fā)電機(jī)和調(diào)用代碼之間來(lái)回傳遞控制來(lái)工作。為了提供迭代的值,代替返回一次或者需要像數(shù)組那樣可以記憶密集的東西,發(fā)電機(jī)可以根據(jù)需要多次產(chǎn)生調(diào)用代碼的值。

Generators可以做Async功能; Async Generators的行為與普通Generators類似,除了每個(gè)產(chǎn)生的值都是Awaitable就是await這樣。

Async Generators

要從Async Generators獲取值或鍵/值對(duì),您可以分別返回HH \ AsyncIteratorHH \ AsyncKeyedIterator。

以下是使用Async實(shí)用功能的示例 usleep()模仿一個(gè)秒針倒計(jì)時(shí)鐘。請(qǐng)注意,在happy_new_year() foreach循環(huán)中我們有語(yǔ)法await as。這是調(diào)用的簡(jiǎn)寫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; 否則你不會(huì)得到迭代值。

注意await as就像打電話一樣await $gen->next(); 但是,await as如果可能,您應(yīng)該始終使用。打電話給AsyncGenerator直接的方法很少需要。還要注意,在異步迭代器await as或調(diào)用next()實(shí)際上返回一個(gè)值(而不是void一般的迭代器)。

發(fā)送和提高

很少需要直接調(diào)用這些方法。await as應(yīng)該是訪問迭代程序返回值的最常見的用法。

您可以使用Generator發(fā)送值并在Generator上send()引發(fā)異常raise()。

如果您正在做這兩件事情,您的Generator必須返回AsyncGenerator。An AsyncGenenator有三種類型的參數(shù)。第一是關(guān)鍵。第二個(gè)是價(jià)值。第三種是傳遞給的類型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"
}

這是如何將異常引發(fā)到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"
}
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)