Async示例
这里有一些表示一系列可能的Async方案的代码示例。显然这并不涵盖所有可能的情况,但他们应该提供一个如何以及如何有效地使用Async的想法。其中一些示例通过其余的Async文档进行分析; 他们再次被添加到合并目的。
基本
此示例显示Async的基本使用者。
<?hh
namespace Hack\UserDocumentation\Async\Examples\Examples\Basic;
// async specifies a function will return an awaitable. Awaitable<string> means
// that the awaitable will ultimately return a string when complete
async function trivial(): Awaitable<string> {
return "Hello";
}
async function call_trivial(): Awaitable<void> {
// These first two lines could be combined into
// $result = await trivial();
// but wanted to show the process
// get awaitable that you can wait for completion
$aw = trivial();
// wait for the operation to complete and get the result
$result = await $aw;
echo $result; // "Hello"
}
call_trivial();
Output
Hello
Joining
要在non-async函数中获得等待的结果,可以使用join()。
<?hh
namespace Hack\UserDocumentation\Async\Guidelines\Examples\Join;
async function join_async(): Awaitable<string> {
return "Hello";
}
// In an async function, you would await an awaitable.
// In a non-async function, or the global scope, you can
// use `join` to force the the awaitable to run to its completion.
$s = \HH\Asio\join(join_async());
var_dump($s);
Closures
您可以使用async closures,包括使用较短的lambda语法。
<?hh
namespace Hack\UserDocumentation\Async\Examples\Examples\Closures;
async function closure_async(): Awaitable<void> {
// closure
$hello = async function(): Awaitable<string> {
return 'Hello';
};
// lambda
$bye = async ($str) ==> $str;
// The call style to either closure or lambda is the same
$rh = await $hello();
$rb = await $bye("bye");
echo $rh . " " . $rb . PHP_EOL;
}
closure_async();
Output
Hello bye
数据获取
这显示了一种组织Async功能的方式,使我们有一个很好的干净的数据依赖图。
<?hh
namespace Hack\UserDocumentation\Async\Guidelines\Examples\DataDependencies;
// So we can use asio-utilities function vm()
class PostData {
// using constructor argument promotion
public function __construct(public string $text) {}
}
async function fetch_all_post_ids_for_author(int $author_id)
: Awaitable<array<int>> {
// Query database, etc., but for now, just return made up stuff
return array(4, 53, 99);
}
async function fetch_post_data(int $post_id): Awaitable<PostData> {
// Query database, etc. but for now, return something random
return new PostData(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
}
async function fetch_comment_count(int $post_id): Awaitable<int> {
// Query database, etc., but for now, return something random
return rand(0, 50);
}
async function fetch_page_data(int $author_id)
: Awaitable<Vector<(PostData, int)>> {
$all_post_ids = await fetch_all_post_ids_for_author($author_id);
// An async closure that will turn a post ID into a tuple of
// post data and comment count
$post_fetcher = async function(int $post_id): Awaitable<(PostData, int)> {
list($post_data, $comment_count) =
await \HH\Asio\v(array(
fetch_post_data($post_id),
fetch_comment_count($post_id),
));
/* The problem is that v takes Traverable<Awaitable<T>> and returns
* Awaitable<Vector<T>>, but there isn't a good value of T that represents
* both ints and PostData, so they're currently almost a union type.
*
* Now we need to tell the typechecker what's going on.
* In the future, we plan to add HH\Asio\va() - VarArgs - to support this.
* This will have a type signature that varies depending on the number of
* arguments, for example:
*
* - va(Awaitable<T1>, Awaitable<T2>): Awaitable<(T1, T2)>
* - va(Awaitable<T1>,
* Awaitable<T2>,
* Awaitable<T3>): Awaitable<(T1, T2, T3)>
*
* And so on, with no need for T1, T2, ... Tn to be related types.
*/
invariant($post_data instanceof PostData, "This is good");
invariant(is_int($comment_count), "This is good");
return tuple($post_data, $comment_count);
};
// Transform the array of post IDs into an array of results,
// using the vm() function from asio-utilities
return await \HH\Asio\vm($all_post_ids, $post_fetcher);
}
async function generate_page(int $author_id): Awaitable<string> {
$tuples = await fetch_page_data($author_id);
$page = "";
foreach ($tuples as $tuple) {
list($post_data, $comment_count) = $tuple;
// Normally render the data into HTML, but for now, just create a
// normal string
$page .= $post_data->text . " " . $comment_count . PHP_EOL;
}
return $page;
}
$page = \HH\Asio\join(generate_page(13324)); // just made up a user id
var_dump($page);
访问MySQL
使用Async mysql扩展来执行数据库连接和查询。
<?hh
namespace Hack\UserDocumentation\Async\Extensions\Examples\MySQL;
use \Hack\UserDocumentation\Async\Extensions\Examples\AsyncMysql\ConnectionInfo as CI;
async function get_connection(): Awaitable<\AsyncMysqlConnection> {
// Get a connection pool with default options
$pool = new \AsyncMysqlConnectionPool(array());
// Change credentials to something that works in order to test this code
return await $pool->connect(
CI::$host,
CI::$port,
CI::$db,
CI::$user,
CI::$passwd,
);
}
async function fetch_user_name(\AsyncMysqlConnection $conn,
int $user_id) : Awaitable<string> {
// Your table and column may differ, of course
$result = await $conn->queryf(
'SELECT name from test_table WHERE userID = %d',
$user_id
);
// There shouldn't be more than one row returned for one user id
invariant($result->numRows() === 1, 'one row exactly');
// A vector of vector objects holding the string values of each column
// in the query
$vector = $result->vectorRows();
return $vector[0][0]; // We had one column in our query
}
async function get_user_info(\AsyncMysqlConnection $conn,
string $user): Awaitable<Vector<Map>> {
$result = await $conn->queryf(
'SELECT * from test_table WHERE name = %s',
$conn->escapeString($user)
);
// A vector of map objects holding the string values of each column
// in the query, and the keys being the column names
$map = $result->mapRows();
return $map;
}
async function async_mysql_tutorial(): Awaitable<void> {
$conn = await get_connection();
if ($conn !== null) {
$result = await fetch_user_name($conn, 2);
var_dump($result);
$info = await get_user_info($conn, 'Fred Emmott');
var_dump($info instanceof Vector);
var_dump($info[0] instanceof Map);
}
}
\HH\Asio\join(async_mysql_tutorial());
Batching
使用重新安排(通过HH\Asio\later())来批处理操作,通过高延迟网络在单个请求中发送多个密钥(例如,网络不是高延迟,而只是随机返回)。
<?hh
namespace Hack\UserDocumentation\Async\Guidelines\Examples\Batching;
// For asio-utilities function later(), etc.
async function b_one(string $key): Awaitable<string> {
$subkey = await Batcher::lookup($key);
return await Batcher::lookup($subkey);
}
async function b_two(string $key): Awaitable<string> {
return await Batcher::lookup($key);
}
async function batching(): Awaitable<void> {
$results = await \HH\Asio\v(array(b_one('hello'), b_two('world')));
echo $results[0] . PHP_EOL;
echo $results[1];
}
\HH\Asio\join(batching());
class Batcher {
private static array<string> $pendingKeys = array();
private static ?Awaitable<array<string, string>> $aw = null;
public static async function lookup(string $key): Awaitable<string> {
// Add this key to the pending batch
self::$pendingKeys[] = $key;
// If there's no awaitable about to start, create a new one
if (self::$aw === null) {
self::$aw = self::go();
}
// Wait for the batch to complete, and get our result from it
$results = await self::$aw;
return $results[$key];
}
private static async function go(): Awaitable<array<string, string>> {
// Let other awaitables get into this batch
await \HH\Asio\later();
// Now this batch has started; clear the shared state
$keys = self::$pendingKeys;
self::$pendingKeys = array();
self::$aw = null;
// Do the multi-key roundtrip
return await multi_key_lookup($keys);
}
}
async function multi_key_lookup(array<string> $keys)
: Awaitable<array<string, string>> {
// lookup multiple keys, but, for now, return something random
$r = array();
foreach ($keys as $key) {
$r[$key] = str_shuffle("ABCDEF");
}
return $r;
}
Polling
您可以在polling循环中使用重新计划,以允许其他等待运行。在服务没有Async功能添加到调度程序的情况下,可能需要一个polling循环。
<?hh
namespace Hack\UserDocumentation\Async\Examples\Examples\Polling;
// For asio-utilities function later(), etc.
// Of course, this is all made up :)
class Polling {
private int $count = 0;
public function isReady(): bool {
if ($this->count++ === 10) {
return true;
}
return false;
}
public function getResult(): int {
return 23;
}
}
async function do_polling(Polling $p): Awaitable<int> {
echo "do polling 1" . PHP_EOL;
// No async function in Polling, so loop until we are ready, but let
// other awaitables go via later()
while (!$p->isReady()) {
await \HH\Asio\later();
}
echo "\ndo polling 2" . PHP_EOL;
return $p->getResult();
}
async function no_polling(): Awaitable<string> {
echo '.';
return str_shuffle("ABCDEFGH");
}
async function polling_example(): Awaitable<void> {
$handles = array(do_polling(new Polling()));
// To make this semi-realistic, call no_polling a bunch of times to show
// that do_polling is waiting.
for ($i = 0; $i < 50; $i++) {
$handles[] = no_polling();
}
$results = await \HH\Asio\v($handles);
}
\HH\Asio\join(polling_example());
Output
do polling 1
..................................................
do polling 2