Я пытаюсь реализовать js-подобные обещания с помощью responsePHP.
Но по некоторым причинам методы, выполняемые синхронно, end_at
печатается только после выполнения обещания.
Код:
function iterate() {
$deferred = new \React\Promise\Deferred();
sleep(2);
$deferred->resolve();
return $deferred->promise();
}
Route::get('test/async', function() {
echo "start execution at ".time()."<br>"; // this executed first
iterate()->then(function($result) {
echo "got result result at ". time() . "<br>"; // this is second
}, function($error) {
}, function ($finally) {
});
echo "end at " . time(); // this is executed only after then().
});
Само обещание не является асинхронным. Само по себе обещание просто предлагает вам возможность запустить некоторый код, а затем другой обратный вызов, основанный на успехе или неудаче, а затем объединить несколько из этих концепций вместе.
Асинхронный код может быть выполнен с использованием модуля, специфичного для вашей задачи. Если вы пытаетесь сделать HTTP-запрос, вы можете использовать ReactPHP«s HTTP-клиент модуль. Если вы хотите выполнить системную команду асинхронно, вы можете рассмотреть возможность использования ребенок-процесс модуль.
Если вы хотите сделать что-то совершенно другое или сделанное на заказ, вам следует абстрагировать работу в собственное асинхронное поведение, подобное Предисловная асинхронная библиотека. Возможно использование модуля обещания для обеспечения коллбэков успеха / неудачи.
Проблема в вашем коде в том, что вы используете функцию блокировки: sleep()
Здесь приведены некоторые блокирующие вызовы:
https://github.com/reactphp/react/wiki/FAQ
Но обещание само по себе асинхронно: оно дает возможность объявить функцию, которая будет вызвана в цепочке, до того, как она будет вызвана.
Фактически, вызывая функцию, вы должны ждать, пока она не будет выполнена, но заявив, что «тогда вы должны запустить это», вы не запускаете ее, как только возвращается первый вызов.
Так
function iterate() {
global $loop;
$deferred = new \React\Promise\Deferred();
$loop->addTimer(2, function () use ($deferred) {
$deferred->resolve();
});
return $deferred->promise();
}
Route::get('test/async', function() {
echo "start execution at ".time()."<br>"; // this executed first
iterate()->then(function($result) {
echo "got result result at ". time() . "<br>"; // this when solved
}, function($error) {
}, function ($finally) {
});
echo "end at " . time(); // this is executed only after iterate() and then() returns.
});
Я предполагал, что у вас есть глобальный $loop = React\EventLoop\Factory::create();
Чаще всего это имеет смысл.
Вот $loop->addTimer()
позвоните немедленно, так iterate()
возвращает обещание, неразрешенный, поэтому метод then()
вызывается немедленно, и последовательное выполнение продолжается с вашим echo end at ...
then()
метод прикрепляет поведение к возвращенному обещанию, не выполняет функцию, переданную в качестве аргумента. Но сначала нужно вернуть обещание, проблема в том, что сон — это не асинхронный сон, а действительно, он спит в течение 2 секунд!
Обратите внимание, что у вас нет спящего аналога в вашем javascript setTimeout()
, который ведет себя так же, как это $loop->addTimer()