首頁  >  文章  >  後端開發  >  PHP協程的thunkify自動執行器的詳細介紹(程式碼)

PHP協程的thunkify自動執行器的詳細介紹(程式碼)

不言
不言轉載
2018-10-09 14:08:272559瀏覽

這篇文章帶給大家的內容是關於PHP協程的thunkify自動執行器的詳細介紹(程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

高階函數

在我們實作自動調度(器)函數前,我們先來理解下高階函數

thunk函數

# 先求值再传参
function func(m){
  return m * 2;     
}
f(x + 5);
// 等同于

# 先传参再求值
var thunk = function () {
  return x + 5;
};

function func(thunk){
  return thunk() * 2;
}

# 这段我们在python或一些语言里,概念叫高阶函数
# 因为php是解释性动态语言,所以函数可以当参数传入
# 这里python,js,php下函数都是可以传参的

PHP版本的thunkify函數

thunkify實作原理:

1、包裝一次原始函數名,然後傳回一個第一次匿名函數(並攜帶包裝函數): return function () use ($func){$args = func_get_args();}

2、然後再取得該匿名函數的參數,並在上一次第一次匿名函數體內傳回一次帶回調參數的第二次匿名函數(並攜帶上一次環境上下文): return function ($callback) use ($args, $func){}

3、呼叫包裝函數,參數為:第一次匿名函數呼叫的參數一個回呼函數

function thunkify($func){
    return function () use ($func) {
        $args = func_get_args();
        return function ($callback) use ($args, $func) {
            array_push($args, $callback);
            return $func(...$args);
        };
  };
};

$printStr = function($p1, $p2, $callback) {
    $callback($p1, $p2);
};

$printStrThunkify = thunkify($printStr);

$printStrThunkify(...["foo", "bar"])(function (...$p) {
    var_dump($p);
});

# output
array(2) {
  [0]=>
  string(3) "foo"
  [1]=>
  string(3) "bar"
}

只能執行一次回呼的thunkify函數

function thunkify($func){
    return function () use ($func) {
        $args = func_get_args();
        return function ($callback) use ($args, $func) {
            // 原本的获取参数,回调会多次执行
            // array_push($args, $callback); 
            // 增加回调只能执行一次
            $callbackCalled = false;
            array_push($args, function (...$params) use ($callback, &$callbackCalled) {
                if ($callbackCalled) return ;
                $callbackCalled = true;
                $callback(...$params);
            });
            return $func(...$args);
        };
    };
};

$printStr = function($p1, $p2, $callback) {
    $callback($p1, $p2);
    $callback($p1, $p2); //我们增加一次回调
};

$printStrThunkify = thunkify($printStr);

$printStrThunkify(...["foo", "bar"])(function (...$p) {
    var_dump($p);
});

# output
array(2) {
  [0]=>
  string(3) "foo"
  [1]=>
  string(3) "bar"
}

看到這裡,你可能還在疑惑,thunkify函數其實只是幫我們包裝了一次有回呼函數的高階函數而已
不過這裡到底有什麼用處呢,在普通場景下確實用戶不大(可能用處單純就在做一些前後置函數包裝也是用處的,類似python的裝飾)
但是,但是,但是在生成器協程裡,Thunkify函數可以用於生成器協程的自動流程管理。

生成器協程的自動執行基礎理解

每一次yield出來的結果都是一個thunk函數的回呼

function thunkify($func){
    return function () use ($func) {
        $args = func_get_args();
        return function ($callback) use ($args, $func) {
            $callbackCalled = false;
            array_push($args, function (...$params) use ($callback, &$callbackCalled) {
                if ($callbackCalled) return ;
                $callbackCalled = true;
                $callback(...$params);
            });
            return $func(...$args);
        };
    };
};

$printStr1 = function($p1, $callback) {
    $callback($p1);
};
$printStr2 = function($p1, $callback) {
    $callback($p1);
};

$printStrThunkify1 = thunkify($printStr1);
$printStrThunkify2 = thunkify($printStr2);

function gen()
{
    global $printStrThunkify1, $printStrThunkify2;

    $r1 = yield $printStrThunkify1("1");
    var_dump($r1);
    $r2 = yield $printStrThunkify2("2");
    var_dump($r2);
}

$gen = gen();

// 手动回调, 模拟自动执行基础理解
$value = $gen->current();
$value(function ($p1) use($gen) {
    $value = $gen->send($p1);
    $value(function ($p1) use($gen) {
        $value = $gen->send($p1);
        var_dump($value);
    });
});

自動執行器

我們這裡只是實作上面的手動回呼執行
增加了一個自動執行器,把生成器協程傳入後講自動執行生成器協程

function thunkify($func){
    return function () use ($func) {
        $args = func_get_args();
        return function ($callback) use ($args, $func) {
            $callbackCalled = false;
            array_push($args, function (...$params) use ($callback, &$callbackCalled) {
                if ($callbackCalled) return ;
                $callbackCalled = true;
                $callback(...$params);
            });
            return $func(...$args);
        };
    };
};

$printStr1 = function($p1, $callback) {
    sleep(2);
    $callback($p1);
};
$printStr2 = function($p1, $callback) {
    sleep(5);
    $callback($p1);
};

$printStrThunkify1 = thunkify($printStr1);
$printStrThunkify2 = thunkify($printStr2);

function gen()
{
    global $printStrThunkify1, $printStrThunkify2;

    $r1 = yield $printStrThunkify1("1");
    var_dump($r1);
    $r2 = yield $printStrThunkify2("2");
    var_dump($r2);
}

function autoCaller(\Generator $gen)
{
    // 注意这里的$next use 引入作用域必须带上&, 否则无法识别
    $next = function ($p1) use ($gen, &$next) {

        if (is_null($p1)) { //此处获取第一次yeild的回调
            $result = $gen->current();
        } else {
            // send后返回的是下一次的yield值
            $result = $gen->send($p1);
        }

        // 是否生成器迭代完成
        // 迭代器生成完成,不再迭代执行(自动执行器返回停止)
        if (!$gen->valid()) {
            return ;
        }

        $result($next);
    };

    $next(null);
}

$gen1 = gen();
//$gen2 = gen();

autoCaller($gen1);
//autoCaller($gen2);

# output
string(1) "1"
string(1) "2"

# 如果我们打开上面的两个sleep()注释
# output

# 等待2秒
string(1) "1"
# 等待5秒
string(1) "2"

# 因为这里我们的thunk里执行的实际函数是同步的代码,所以整体是阻塞的后续代码执行的

總結

只要執行autoCaller 函數,生成器就會自動迭代完成。這樣一來,非同步操作不僅可以寫得像同步操作,而且一行程式碼就可以執行。

Thunkify函數並不是 生成器協程 函數自動執行的唯一方案。

因為自動執行的關鍵是,必須有一個機制,自動控制 產生器協程 函數的流​​程,接收和交還程式的執行權。

回呼函數可以做到這一點,Promise 物件也可以做到這一點。

以上是PHP協程的thunkify自動執行器的詳細介紹(程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除