Using callback-style from generators
Although it is easier to work with generator functions,
ultimately, you will need to work with functions that do not use await-generator.
In that case, callbacks are easier to use.
A callback $resolve
can be acquired using Await::promise
.
function a(Closure $callback): void {
// The other function that uses callbacks.
// Let's assume this function will call $callback("foo") some time later.
}
function main(): Generator {
return yield from Await::promise(fn($resolve) => a($resolve));
}
Some callback-style async functions may accept another callback for exception handling. This callback can be acquired by taking a second parameter $reject
.
function a(Closure $callback, Closure $onError): void {
// The other function that uses callbacks.
// Let's assume this function will call $callback("foo") some time later.
}
function main(): Generator {
return yield from Await::promise(fn($resolve, $reject) => a($resolve, $reject));
}
Example
Let's say we want to make a function that sleeps for 20 server ticks, or throws an exception if the task is cancelled:
use pocketmine\scheduler\Task;
public function sleep(): Generator {
yield from Await::promise(function($resolve, $reject) {
$task = new class($resolve, $reject) extends Task {
private $resolve;
private $reject;
public function __construct($resolve, $reject) {
$this->resolve = $resolve;
$this->reject = $reject;
}
public function onRun(int $tick) {
($this->resolve)();
}
public function onCancel() {
($this->reject)(new \Exception("Task cancelled"));
}
};
$this->getServer()->getScheduler()->scheduleDelayedTask($task, 20);
});
}
This is a bit complex indeed, but it gets handy once we have this function defined! Let's see what we can do with a countdown:
function countdown($player) {
for($i = 10; $i > 0; $i--) {
$player->sendMessage("$i seconds left");
yield from $this->sleep();
}
$player->sendMessage("Time's up!");
}
This is much simpler than using ClosureTask
in a loop!