Search code examples
asynchronoushhvmhacklang

Hacklang async code example?


How modify the following code to get article data and top articles asynchronously in hack ?

class ArticleController
{
    public function viewAction()
    {
        // how get
        $article = $this->getArticleData();
        $topArticles = $this->getTopArticles();
    }

    private function getArticleData() : array
    {
        // return article data from db
    }

    private function getTopArticles() : array
    {
        // return top articles from db
    }
}

Solution

  • HHVM 3.6 and newer

    async functions info

    The two HHVM PHP language keywords that enable async functions are async and await. async declares a function as asynchronous. await suspends the execution of an async function until the result of the asynchronous operation represented by await is available. The return value of a function that await can be used upon is an object that implements Awaitable<T>.

    You have an example in the documentation (1). There is a discussion about asynchronous functions in the language specification as well (2).

    It actually took me some time to realize how to use and call the asynchronous functions, so I think you will find some more info useful.

    We have these two functions: foo() and bar().

    async function foo(): Awaitable<void> {
      print "executed from foo";
    }
    async function bar(int $n): Awaitable<int> {
      print "executed from bar";
      return $n+1;
    }
    

    Let's experiment some ways to call these two functions:

    foo();                  // will print "executed from foo"
    bar(15);                // will print "executed from bar"
    $no1 = bar(15);         // will print "executed from bar"
    print $no1;             // will output error, because $number is not currently an `int`; it is a `WaitHandle`
    $no2 = bar(15)->join(); // will print "executed from bar"
    print $no2;             // will print 16
    

    AsyncMysqlClient tips

    The connection to a MySQL database is made with AsyncMysqlClient::connect asynchronous function which returns an ExternalThreadEventWaitHandle to an AsyncMysqlConnection.

    You can perform query or queryf on an AsyncMysqlConnection. Note: the data you send to a queryf is properly escaped by the function.

    A query you perform on an AsyncMysqlConnection returns either an AsyncMysqlQueryResult (when the query performs ok) or AsyncMysqlQueryErrorResult (if the query goes wrong; then you can treat errors with the mysql_error(), mysql_errno() and failureType() members of this class). Both AsyncMysqlQueryResult and AsyncMysqlQueryErrorResult extend AsyncMysqlResult abstract class.


    Below is a probable implementation of your class:

    class ArticleController {
      private AsyncMysqlConnection $connection;
      public async function viewAction(int $articleId): Awaitable<void> {
        $this->connection = await AsyncMysqlClient::connect( /* connection data */ );
        $article = await $this->getArticleData($articleId);
      }
      public async function getArticleData(int $id): Awaitable<?Vector> {
        $articleDataQuery = await $this->connection->queryf("SELECT * FROM articles WHERE id %=d", $id);
        if($articleDataQuery instanceof AsyncMysqlQueryErrorResult) {
          throw new Exception("Error on getting data: ".$articleDataQuery->mysql_error());
        }
    
        // Considering that $id represents a unique id in your database, then
        // you are going to get only one row from your database query
        // so you return the first (and only) row in the query result
        if($articleDataQuery->numRows() == 1) {
          return $articleDataQuery->mapRowsTyped()[0];
        }
        return null;
      }
    } 
    

    P.S. I hope it is not too late for this answer and I hope it helps you. If you consider this useful, please, accept it.