amonite/async

PHP 异步、await 和 Promise 类,准备运行异步方法

dev-master 2020-02-24 11:35 UTC

This package is not auto-updated.

Last update: 2024-10-02 16:11:33 UTC


README

PHP 的异步范式!

现在可以使用了!只需使用 await 函数开始您的异步环境,然后使用下面的函数和类做您想做的事情!

安装

您可以选择以下方法:

  1. 运行 composer require amonite/async,或者
  2. async.phar 文件复制到您的项目目录中,并使用 require 加载它。

上下文

作为一名 JavaScript 开发者,我喜欢异步范式。我决定将这种范式导出,以便 PHP 可以利用它:现在,可以创建在等待它们加载数据时处理信息的库。

非阻塞事件

在 PHP 中,默认情况下,许多函数会在脚本结束时停止。这对常见的算术函数很有帮助,但您还可以在您从数据库请求数据时观察到它(无论您的软件是什么),当您从您的计算机或互联网加载文件时,当您使用某些套接字或运行命令行时。

当您的脚本等待另一个软件时,那么您的进程就会停止。当客户端开始向服务器发送请求时,它会等待上一个客户端脚本结束。当成千上万的客户端都在等待单个客户端,而该客户端正在等待一个耗时 30 秒以上的单个重 SQL 请求时,服务器可能会崩溃,就像遭受了 DDoS 攻击一样。

通过使用这个库,我将开发一个非阻塞 HTTP 服务器,然后是 HTTPS、HTTP/2、SQL 和文件系统读写器。希望这能有所帮助!

用法

  1. 理解 await/async 环境
    1. 创建 await 环境
    2. 触发异步事件
    3. 示例:异步套接字
  2. 使用 Promise 类
  3. 使用 Async 类
  4. 使用 await 和 async 的组合

理解 await/async 环境

函数 await 创建一个环境,允许注册一个 async 事件。异步事件是一个非阻塞函数,它在写入文件函数、读取文件函数、连接启动函数等结束时继续脚本。

创建 await 环境

await($fn_env) : mixed 中的函数 $fn_env 创建了一个异步环境,并等待(许多次)所有异步事件结束。在此函数中,脚本在它的异步函数结束之前被阻塞。如果 $fn_env 返回一个 Promise,则 await 函数返回其已解决的答案或抛出其已拒绝的错误。

文件包装 test/wrap.php

<?php

require_once "async.phar";
//require_once "index.php";

await(function () {

  require "await.php";
  require "async.php";

});

触发异步事件

async($test, $then) : null 中的函数 $testawait 函数的每次迭代中执行,直到它返回一个真值(或抛出一个错误)。

这里的文件 wrap 允许使用异步函数的所有必需文件。

在下一个文件中,我同时使用了异步和 await。首先注册异步,然后等待 await 函数结束和异步事件,然后注册最后一个异步函数。

文件 test/await.php

<?php

$date = microtime(true);

async(function () use ($date) { if($date + 4 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "first resolved\n"; });
echo "first entered\n";

await(function () use ($date) {

  async(function () use ($date) { if($date + 3 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "second resolved\n"; });
  echo "second entered\n";

  async(function () use ($date) { if($date + 2 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "third resolved\n"; });
  echo "third entered\n";

});

async(function () use ($date) { if($date + 1 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "fourth resolved\n"; });
echo "fourth entered\n";

控制台将打印

(t+0s) > first entered
(t+0s) > second entered
(t+0s) > third entered
(t+2s) > third resolved
(t+3s) > second resolved
(t+3s) > fourth entered
(t+3s) > fourth resolved
(t+4s) > first resolved

使用 Promise 类

Promises 通常用于平坦异步和冗长的函数。它遵循 JavaScript 标准。

文件 test/promise.php

<?php

require_once __DIR__ . "/../index.php";
$date = microtime(true);

$p = new Async\Promise(function ($resolve, $reject) use ($date) {
  while (microtime(true) < $date + 2) time_nanosleep(0,1);
  $resolve();
});

$p->then(function () { echo "I waited for 2 seconds.\n"; throw new Error("test error"); });
$p->catch(function () { echo "An error occurred in the promise or in then() function.\n"; });


var_dump(await(function () use ($date) {
  return Async\Promise::all(array(
    Async\Promise::resolve(3.14),
    Async\Promise::async(function () { return "Fibonacci"; }),
    Async\Promise::async(function () use ($date) {
      if (microtime(true) > $date + 3) return "Time elapsed!";
    })
  ));
}));

(!) 在此示例中,代码在 2 秒钟后才会阻塞,然后等待每个 Promise 被解决。

使用 Async 类

如果您想完全管理您的异步事件,您可能更喜欢使用 Async 类。

文件 test/async.php

<?php

$delay = microtime(true) + 3;

$test = function () use ($delay) {
  if ($delay < microtime(true)) return "delay";
};

$then = function ($err, $m) {
  if ($err) {
    echo "$err err";
  } else {
    echo "$m then\n";
  }
};

new Async\Async($test, $then);

在阻塞上下文中的返回

现在你已经处理了异步项目,你可以回到同步脚本中,使用它的阻塞函数和它的时间浪费。幸运的是!你可以在其环境中通过返回一个 Promise 来获取 await 值。函数 await 能够获取返回的 Promise 的结果。警告:如果您的 Promise 被拒绝,那么 await 将会抛出错误!

文件 test/await_async.php

<?php

require_once "index.php";

$res = await(function () {

  // do async stuff
  // ...

  return new Async\Promise(function (\Closure $resolve, \Closure $reject) {

    $d = 3;
    $t = microtime(true) + $d;
    async(function () use ($t){
      return microtime(true) > $t;
    }, function ($err) use ($resolve, $reject, $d) {
      if ($err) $reject($err);
      else $resolve("Now $d seconds elapsed. Well done!\n");
    });

  });
});

echo $res;

文档

函数 await

创建一个异步实例并在 await 环境中注册它。

  • 语法: await (\Closure $env) : mixed
  • 参数: $env {Closure.<$self {Closure}>} 是一个用于注册每个异步实例的函数。
  • 返回: 如果 $env 返回一个已解析的 Promise,则返回其结果。
  • 抛出: 如果 $env 抛出或返回一个被拒绝的 Promise(返回其错误)。

函数 async

注册一个在每个 await 环境的每个滴答等待解决或拒绝的函数或超时错误(如果没有设置,则为 3000ms)。

  • 语法: async (\Closure $test, \Closure $then = null) : null
  • 参数
    • $fn {Closure.<>} 是在每个 await 环境中执行的第一个函数,直到它返回一个真值,作为第二个参数发送到 $then;如果函数抛出错误,则将其作为第一个参数发送到 $then
    • $then {Closure.<$error {Throwable|null}, $result {*}>} 是在 $fn 事件发生后执行的函数;当 $fn 解决时,结果作为第二个参数 $result 发送,或者当 $fn 抛出错误时,错误作为第一个参数 $error 发送(否则 $errornull)。
  • 返回: null
  • 抛出: 如果此实例不是在 await 环境中创建的,则抛出 {Async\AsyncError}

Async\Await

方法 __construct

创建一个异步实例并在 await 环境中注册它。

  • 语法: $await = new Async\Await (\Closure $env) : {Async\Await}
  • 参数: $env {Closure.<$self {Closure}>} 是一个用于注册每个异步实例的函数。
  • 返回: {Async\Await} 新实例。
  • 抛出: 如果 $env 抛出,则抛出 {Throwable}

方法 env

在 await 环境中运行函数,并将 $env 中找到的异步实例添加进去。

  • 语法: $await->env (\Closure $env) : mixed
  • 参数: $env {Closure.<$self {Closure}>} 是一个用于注册每个异步实例的函数。
  • 返回: 如果 $env 返回一个已解析的 Promise,则返回其结果。
  • 抛出: 如果 $env 抛出或返回一个被拒绝的 Promise(返回其错误)。

静态 isAwaitContext

判断您的脚本是否当前在 await 环境中。

  • 语法: Async\Await::isAwaitContext() : boolean
  • 返回: {boolean} 如果脚本在 await 环境中,则为 true

静态 add

静态 remove

Async\Async

方法 __construct

注册一个在每个 await 环境的每个滴答等待解决或拒绝的函数或超时错误(如果没有设置,则为 3000ms)。

  • 语法: $async = new Async\Async (\Closure $test, \Closure $then = null) : Async\Async
  • 参数
    • $test {Closure.<>} 是在每个 await 环境中执行的第一个函数,直到它返回一个真值,作为第二个参数发送到 $then;如果函数抛出错误,则将其作为第一个参数发送到 $then
    • $then {Closure.<$error {Throwable|null}, $result {*}>} 是在 $test 事件发生后执行的函数;当 $test 解决时,结果作为第二个参数 $result 发送,或者当 $test 抛出错误时,错误作为第一个参数 $error 发送(否则 $errornull)。
  • 返回: {Async\Async} 新实例。
  • 抛出: 如果此实例不是在 await 环境中创建的,则抛出 {Async\AsyncError}

方法 test

验证 $test 函数返回一个真值:如果是真值,则执行 $then 函数;否则什么也不做;或者捕获错误并发送到 $then 函数。

  • 语法: $async->test()
  • 返回: {Async\Async} 此实例。

Async\Promise

方法 __construct

创建一个 Promise 实例。Promise 帮助将异步函数扁平化到单个实例中。

  • 语法: $prom = new Async\Promise(\Closure $fn) : Async\Promise
  • 参数: $fn {Closure.<$resolve {Closure.<$result {*}>}, $reject {Closure.<$error {Throwable}>}>} 是等待 $resolve 执行或 $reject 执行或错误抛出的函数。
  • 返回值: {Async\Promise.<$result {*}>.<$error {Throwable}>} 新实例。

方法 then

注册一个函数,当Promise解析时执行。

  • 语法: $prom->then(\Closure $fn)
  • 参数: $fn {Closure.<$result {*}>} 是当Promise解析时执行的函数。将$result值传递给$resolve
  • 返回值: {Async\Promise} 自身实例。

方法 catch

注册一个函数,当Promise拒绝时执行。

  • 语法: $prom->then(\Closure $fn)
  • 参数: $fn {Closure.<$error {Throwable}>} 是当Promise拒绝时执行的函数。将$error值传递给$reject
  • 返回值: {Async\Promise} 自身实例。

方法 finally

注册一个函数,在Promise解析或拒绝后执行。

  • 语法: $prom->then(\Closure $fn)
  • 参数: $fn {Closure.<$result {*}>} 是在Promise解析或拒绝后执行的函数。将$result值传递给$resolve或在$reject中。
  • 返回值: {Async\Promise} 自身实例。

静态 resolve

创建已解析Promise的有用函数。

  • 语法: Async\Promise::resolve($result {*})
  • 参数: $result {*} 是解析的值
  • 返回值: {Async\Promise} 新实例。

静态 reject

创建已拒绝Promise的有用函数。

  • 语法: Async\Promise::resolve($error {*})
  • 参数: $error {Throwable} 是拒绝的值
  • 返回值: {Async\Promise} 新实例。

静态 all

创建等待Promise列表$list解析的有用函数。

  • 语法: Async\Promise::all($list)
  • 参数: $list {Array.<{Async\Promise} ...>} 应该解析的Promise列表。
  • 返回值: {Array.<{*} ...>} 每个Promise的答案。

静态 any

创建等待Promise列表$list中任何一个解析的有用函数。

  • 语法: Async\Promise::any($list)
  • 参数: $list {Array.<{Async\Promise} ...>} 可解析的Promise列表。
  • 返回值: {*} 第一个解析的Promise的答案。