异步执行

    目录

    imi v2.0.7 起开始支持在方法上声明注解,让这个方法可以以异步的方式调用,并且支持选择等待获取返回值。

    此特性所有环境都可使用,但仅在 Swoole 下才是真异步

    注解

    @Async

    imi v2.0.7 支持在方法上使用 @Async 注解,让这个方法被正常调用时是异步调用。

    @Async 注解类:\Imi\Async\Annotation\Async

    在 Swoole 下,方法被调用时,会立即创建一个协程并执行,只有协程挂起时,才会继续执行当前代码。

    @Defer

    imi v2.1.45 起支持。

    @Defer 注解类:\Imi\Async\Annotation\Defer

    在 Swoole 下,会在协程关闭之前 (即协程函数执行完毕时) 进行调用,就算抛出了异常,已注册的 defer 也会被执行。

    @DeferAsync

    imi v2.1.45 起支持。

    @DeferAsync 注解类:\Imi\Async\Annotation\DeferAsync

    在 Swoole 下,会在协程关闭之前 (即协程函数执行完毕时) 进行调用,调用时会创建一个协程并立即执行,代码所在上下文是一个新的协程。

    使用示例

    @Async@Defer@DeferAsync 的使用方法完全一致,仅执行顺序不同。

    异步执行无返回值

    定义:

    <?php
    
    declare(strict_types=1);
    
    namespace Imi\Swoole\Test\Component\Async;
    
    use Imi\Async\Annotation\Async;
    use Imi\Async\AsyncResult;
    use Imi\Async\Contract\IAsyncResult;
    use Imi\Bean\Annotation\Bean;
    
    class AsyncTester
    {
        /**
         * @Async
         */
        public function test1(): void
        {
            // 这里的代码是异步执行的
            sleep(1);
        }
    }

    调用:

    $asyncTester = \Imi\App::getBean(AsyncTester::class);
    $result = $asyncTester->test1();
    // 下面的代码会立即执行,而不是等待 1 秒后
    // ...
    $result->get(); // 等待异步执行完毕
    $result->get(0.1); // 等待异步执行完毕,超时时间为 0.1 秒,超时则抛出异常

    异步执行有返回值

    定义:

    <?php
    
    declare(strict_types=1);
    
    namespace Imi\Swoole\Test\Component\Async;
    
    use Imi\Async\Annotation\Async;
    use Imi\Async\AsyncResult;
    use Imi\Async\Contract\IAsyncResult;
    use Imi\Bean\Annotation\Bean;
    
    class AsyncTester
    {
        /**
         * 如果一定要声明方法返回值类型,必须声明为 IAsyncResult
         * 
         * @Async
         */
        public function test2(float $a, float $b): IAsyncResult
        {
            return new AsyncResult($a + $b);
        }
    
        /**
         * 不声明方法返回值类型也可以
         * 
         * @Async
         *
         * @return float|IAsyncResult
         */
        public function test3(float $a, float $b)
        {
            return $a + $b;
        }
    }

    调用:

    $asyncTester = \Imi\App::getBean(AsyncTester::class);
    $asyncTester->test2(1, 2)->get(); // 3
    $asyncTester->test3(1, 2)->get(); // 3

    捕获异常

    调用:

    $asyncTester = \Imi\App::getBean(AsyncTester::class);
    try {
        $asyncTester->test1()->get(0.01);
    } catch (\Imi\Async\Exception\AsyncTimeoutException $te) {
        // 捕获异步超时异常
    } catch (\Throwable $th) {
        // 捕获执行期间其它异常
    }