Promise-Based Cache With ReactPHP · @zhukserega
source link: http://seregazhuk.github.io/2017/09/15/reactphp-cache/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
In the previous article, we have already touched caching (when caching DNS records). It is an asynchronous promise-based Cache Component. The idea behind this component is to provide a promise-based CacheInterface
and instead of waiting for a result to be retrieved from a cache the client code gets a promise. If there is a value in a cache the fulfilled with this value promise is returned. If there is no value by a specified key the rejected promise returns.
Interface
The component has one simple in-memory ArrayCache
implementation of CacheInterface
. The interface is pretty simple and contains three methods: get($key)
, set($key, $value)
and delete($key)
:
<?php
namespace React\Cache;
interface CacheInterface
{
// @return React\Promise\PromiseInterface
public function get($key);
public function set($key, $value);
public function delete($key);
}
Set/Get
Let’s try it to see how it works. At first, we put something in cache:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
Method set($key, $value)
simply sets the value of the key foo
to bar
. If this key already exists the value will be overridden.
The next step is to get foo
value back from the cache. Before calling get($key)
take a look at the CacheInterface
:
<?php
// @return React\Promise\PromiseInterface
public function get($key);
Notice, that get($key)
method doesn’t return the value from cache, instead, it returns a promise. Which means that we should use promise done()
method to attach onFulfilled handler and actually retrieve the value from cache:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
$cache->get('foo')
->done(function($value){
var_dump($value); // outputs 'bar'
});
In the previous example actually, there is no need to create a callback simply to call
var_dump
function inside. You can pass a string right indone()
method and everything will work exactly the same:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
// outputs 'bar'
$cache->get('foo')->done('var_dump');
Fallback
It may occur that there is no value in a cache. To catch this situation we should use promise otherwise()
method and attach an onRejected handler:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
$cache
->get('baz')
->otherwise(function(){
echo "There is no value in cache";
});
The last two examples can be merged and rewritten with one promise then()
call which accepts both onFulfilled and onRejected handlers:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
$cache->get('baz')->then(
function($value) {
var_dump($value);
},
function() {
echo "There is no value in cache";
});
With this approach, we can easily provide a fallback value for situations when there is no value in a cache:
<?php
$cache = new React\Cache\ArrayCache();
$cache->set('foo', 'bar');
$data = null;
$cache->get('baz')
->then(
function($value) use (&$data) {
$data = $value;
},
function() use (&$data) {
$data = 'default';
}
);
echo $data; // outputs 'default'
If there is a value in a cache the first callback is triggered and this value will be assigned to $data
variable, otherwise the second callback is triggered and $data
variable gets 'default'
value.
Fallbacks chain
The onRejected handler can itself return a promise. Let’s create a callback with a new promise. For example, this callback tries to fetch some data from a database. On success the promise is fulfilled with this data. If there is no required data in a database we return a some default value. Here is a new fallback callback:
<?php
$getFromDatabase = function() {
$resolver = function(callable $resolve, callable $reject) {
return $resolve('some data from database');
};
return new React\Promise\Promise($resolver);
};
A quick overview. Our promise has a resolver handler. This handler accepts two callbacks: one to fulfill the promise with some value, and another - to reject a promise. In our example we immediately fulfill the promise with a string 'some data from database'
.
The next step is to replace the onRejected handler for a promise which was returned when we call get($key)
method:
<?php
$cache->get('baz')
->then(
function($value) use (&$data) {
$data = $value;
},
$getFromDatabase
);
In the snippet above $getFromDatabase
callback is triggered when there is no value in cache. This callback returns a promise, so we can continue chaining and attach one more then()
method:
<?php
$data = null;
$cache->get('baz')
->then(
function($value) use (&$data) {
$data = $value;
},
$getFromDatabase
)
->then(function($value) use (&$data) {
$data = $value;
});
echo $data;
As you remember our promise is fulfilled with 'some data from database'
string. This value will be passed into the last then
callback. As a result, this string will be printed by echo
.
Delete
To delete something from cache simply call delete($key)
method and specify a key to be deleted.
Conclusion
The Cache Component comes with one simple in-memory ArrayCache
implementation. It simply stores all values in array in memory:
<?php
class ArrayCache implements CacheInterface
{
private $data = array();
public function get($key)
{
if (!isset($this->data[$key])) {
return Promise\reject();
}
return Promise\resolve($this->data[$key]);
}
public function set($key, $value)
{
$this->data[$key] = $value;
}
public function delete($key)
{
unset($this->data[$key]);
}
}
But there are several more implementations:
You can find examples from this article on GitHub.
This article is a part of the ReactPHP Series.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK