Generators
Generators(发电机)提供了一种更紧凑的写入迭代程序的方法。发电机通过在发电机和调用代码之间来回传递控制来工作。为了提供迭代的值,代替返回一次或者需要像数组那样可以记忆密集的东西,发电机可以根据需要多次产生调用代码的值。
Generators可以做Async功能; Async Generators的行为与普通Generators类似,除了每个产生的值都是Awaitable就是await这样。
Async Generators
要从Async Generators获取值或键/值对,您可以分别返回HH \ AsyncIterator或HH \ 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"
}