Przejdź do głównej treści

Klasa Promise w PHP i jej zastosowanie

Klasa Promise w PHP i jej zastosowanie

Programowanie asynchroniczne zyskuje coraz większe znaczenie w nowoczesnym tworzeniu aplikacji, szczególnie tych pracujących w środowiskach serwerowych i obsługujących dużą liczbę żądań jednocześnie. W świecie PHP, który tradycyjnie był skoncentrowany na synchronicznym wykonywaniu kodu, asynchroniczność zaczęła odgrywać istotną rolę wraz z pojawieniem się nowych bibliotek i rozszerzeń. Jednym z kluczowych narzędzi w tym zakresie jest klasa Promise.

Co to jest Promise?

Klasa Promise to abstrakcja pozwalająca zarządzać operacjami asynchronicznymi w prosty i efektywny sposób. Podstawowym celem Promise jest przekazanie wyniku operacji asynchronicznej (sukcesu lub błędu) w przyszłości, gdy operacja ta zostanie zakończona.

Promise można wyobrazić sobie jako kontrakt, który może zostać spełniony (“zrealizowany”) lub odrzucony (“zawiedziony”). W PHP Promise umożliwiają efektywne zarządzanie kodem asynchronicznym, eliminując potrzebę skomplikowanych zagnieżdżeń funkcji zwrotnych.

Przykłady zastosowania klasy Promise

  1. Równoczesne żądania HTTP: W aplikacjach sieciowych można wykorzystać Promise do wykonywania wielu żądań HTTP jednocześnie, np. w celu pobrania danych z różnych API.
  2. Operacje na plikach: Promise pozwalają na asynchroniczne odczytywanie i zapisywanie plików bez blokowania głównego wątku aplikacji.
  3. Bazy danych: Możliwe jest wykonywanie równoległych zapytań do bazy danych.
  4. Procesy zewnętrzne: Promise nadają się do obsługi długotrwałych procesów zewnętrznych.

Implementacja Promise w PHP

W standardowej bibliotece PHP nie znajdziemy jeszcze natywnej klasy Promise, jednak istnieje kilka popularnych bibliotek, które dostarczają tę funkcjonalność, takich jak:

  • Guzzle Promises: Rozszerzenie popularnej biblioteki HTTP Guzzle o obsługę Promise.
  • ReactPHP: Framework do programowania asynchronicznego, który wspiera pracę z Promise.
  • Amp: Narzędzie dedykowane asynchronicznym operacjom w PHP.

Przykład klasy Promise

class Promise
{
    private $state = 'pending';
    private $value = null;
    private $onFulfilled = [];
    private $onRejected = [];

    public function __construct(callable $executor)
    {
        try {
            $executor(
                [$this, 'resolve'],
                [$this, 'reject']
            );
        } catch (Exception $e) {
            $this->reject($e);
        }
    }

    public function then(callable $onFulfilled = null, callable $onRejected = null)
    {
        return new self(function ($resolve, $reject) use ($onFulfilled, $onRejected) {
            $this->handle(
                function ($value) use ($onFulfilled, $resolve, $reject) {
                    try {
                        $resolve($onFulfilled($value));
                    } catch (Exception $e) {
                        $reject($e);
                    }
                },
                function ($reason) use ($onRejected, $resolve, $reject) {
                    if ($onRejected) {
                        try {
                            $resolve($onRejected($reason));
                        } catch (Exception $e) {
                            $reject($e);
                        }
                    } else {
                        $reject($reason);
                    }
                }
            );
        });
    }

    public function resolve($value)
    {
        if ($this->state !== 'pending') {
            return;
        }

        $this->state = 'fulfilled';
        $this->value = $value;

        foreach ($this->onFulfilled as $callback) {
            call_user_func($callback, $value);
        }
    }

    public function reject($reason)
    {
        if ($this->state !== 'pending') {
            return;
        }

        $this->state = 'rejected';
        $this->value = $reason;

        foreach ($this->onRejected as $callback) {
            call_user_func($callback, $reason);
        }
    }

    private function handle(callable $onFulfilled, callable $onRejected)
    {
        if ($this->state === 'fulfilled') {
            call_user_func($onFulfilled, $this->value);
        } elseif ($this->state === 'rejected') {
            call_user_func($onRejected, $this->value);
        } else {
            $this->onFulfilled[] = $onFulfilled;
            $this->onRejected[] = $onRejected;
        }
    }

    public static function all(array $promises)
    {
        return new self(function ($resolve, $reject) use ($promises) {
            $results = [];
            $remaining = count($promises);

            if ($remaining === 0) {
                $resolve([]);
                return;
            }

            foreach ($promises as $index => $promise) {
                $promise->then(
                    function ($value) use (&$results, &$remaining, $index, $resolve) {
                        $results[$index] = $value;
                        $remaining--;

                        if ($remaining === 0) {
                            $resolve($results);
                        }
                    },
                    function ($reason) use ($reject) {
                        $reject($reason);
                    }
                );
            }
        });
    }
}

// Użycie klasy
$promise1 = new Promise(function ($resolve) {
    $resolve("Promise 1 fulfilled");
});

$promise2 = new Promise(function ($resolve) {
    $resolve("Promise 2 fulfilled");
});

Promise::all([$promise1, $promise2])->then(function ($results) {
    print_r($results);
}, function ($error) {
    echo "Error: $error";
});

$resolvedPromise = new Promise(function ($resolve, $reject) {
    $resolve("Resolved successfully");
});

$resolvedPromise->then(function ($value) {
    echo "Resolved value: $value\n";
});

$rejectedPromise = new Promise(function ($resolve, $reject) {
    $reject("An error occurred");
});

$rejectedPromise->then(null, function ($reason) {
    echo "Rejected reason: $reason\n";
});

Prosty przykład z wykorzystaniem Guzzle Promises

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client();

$promise1 = $client->getAsync('https://api.example.com/data1');
$promise2 = $client->getAsync('https://api.example.com/data2');

// Równoczesne uruchomienie obu żądań
$results = Promise\settle([$promise1, $promise2])->wait();

foreach ($results as $result) {
    if ($result['state'] === 'fulfilled') {
        echo $result['value']->getBody();
    } else {
        echo "Error: " . $result['reason'];
    }
}

W powyższym kodzie równocześnie wykonywane są dwa żądania HTTP, a wyniki są obsługiwane po zakończeniu operacji.

Zalety korzystania z Promise

  • Zwiększona wydajność: Możliwość wykonywania wielu operacji jednocześnie.
  • Czytelny kod: Eliminacja zagnieżdżeń funkcji zwrotnych (tzw. callback hell).
  • Obsługa błędów: Centralne zarządzanie błędami operacji asynchronicznych.

Podsumowanie

Klasa Promise stanowi potężne narzędzie umożliwiające efektywne zarządzanie operacjami asynchronicznymi w PHP. Choć natywna obsługa asynchroniczności nadal jest ograniczona, biblioteki takie jak Guzzle, ReactPHP czy Amp dostarczają potężnych mechanizmów dla programistów. Warto rozważyć ich wykorzystanie w projektach wymagających wysokiej wydajności i responsywności.

5 lutego 2025 30

Kategorie

Tagi

php

Ocena wpisu

Dziękujemy!
()

Powiązane wpisy


Używam plików cookie

Moja strona wykorzystuje niezbędne pliki cookie i local storage, które są konieczne do prawidłowego działania strony i świadczenia usług. Możesz dowiedzieć się więcej w mojej polityce prywatności.