在讀這篇文章之前,希望你至少已經:
如果你有以下的知識儲備更好,但不強求:
如果你在某段程式碼可能需要做不只一筆的 http 請求,
而且它們彼此之間沒有先後關係,那這東西就蠻適合你的~
<?php
// pseudo code
$ids = range(1, 100);
$urls = array_map('generateUrl', $ids);
batchAsyncRequest($urls);
qk();
eatCookie();
drinkTea();
buyHouseAtEasternMarket();
buySaddleAtWesternMarket();
不行~
管你是想 qk,還是去西市買鞍韉都還是要等到 batchAsyncRequest() 跑完!
php 本身就沒有 async programming 這回事,
但要同時對不同 stream 操作還是辦得到的~[1]
「-」只是佔位符;「/」代表實際執行的時間
原本你可能是這樣的:
程式碼 | 時間軸 |
---|---|
something() | //////////---------------------------- |
request 1 | ---------////------------------------ |
request 2 | -------------////-------------------- |
request 3 | -----------------////---------------- |
request 4 | ---------------------////------------ |
request 5 | -------------------------////-------- |
something() | -----------------------------//////// |
調整後你其實是可以這樣的:
程式碼 | 時間軸 |
---|---|
something() | //////////------------ |
request 1 | ---------////-------- |
request 2 | ---------////-------- |
request 3 | ---------////-------- |
request 4 | ---------////-------- |
request 5 | ---------////-------- |
something() | -------------//////// |
參考自官方文件
<?php
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;
$client = new GuzzleHttp\Client([
'base_uri' => 'https://foo.com/api/'
]);
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
function (ResponseInterface $res) {
echo $res->getStatusCode() . "\n";
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
}
);
相信大家直到 $promise->then 之前大概都沒啥問題。
那麼,promise、then() 是什麼鬼呢?
promise 是普羅米修斯的近親嗎?
參考自 MDN 的說法,Promise 用來代表「異步操作回傳的結果」。
就像是如果你要透過 ajax 從你老闆那得到五串香蕉,
那老闆這邊就先給你一個 promise,這個 promise 代表我最後一定會給你點什麼。
在 js 裡面,
promise 的 then()
代表 promise 得到結果的時候要作的事情;
而 catch()
代表 promise 執行中發生錯誤時要作的處理。
不過在 GuzzleHttp 這邊,$promise->then()
的第一個參數就是成功時要作的事情;
第二個參數就是發生錯誤時要作的處理(就是 catch())。
這個是補充知識
一個 Promise 會有「擱置(pending)、實現(fulfilled)、拒絕(rejected)」三種狀態。
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// 開啟多筆連線(這邊不會出現 blocking)
$promises = [
'image' => $client->getAsync('/image'),
'png' => $client->getAsync('/image/png'),
'jpeg' => $client->getAsync('/image/jpeg'),
'webp' => $client->getAsync('/image/webp')
];
// 你可以執行這一行,以等待所有連線成功。但只要有一筆連線失敗,
// 就會丟出 ConnectException
$results = Promise\unwrap($promises);
// 你也可以執行這一行,以等待所有連線完成,即使它們有任何一筆失敗都沒關係。
$results = Promise\settle($promises)->wait();
// 在這邊你已經收到所有 response 了,你便能用 key 存取到對應的 response
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]
如果你想要控制同時開啟的連線在一定的數量,
那麼你就需要參考官方文件的這個章節
其中的重點在原生的 yield 語法~
(return 一次之後該函式便暫停執行,直到下次執行這個函式)
除此之外大概大家也都沒什麼困難,
收工~