Async示例

2018-10-13 11:54 更新

这里有一些表示一系列可能的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


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号