Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

An async map function

Original – by Freek Van der Herten – 3 minute read

Laravel has an excellent Collection class that has many useful operations. The class is also macroable. This means that you can add function to it at runtime by calling macro on it and passing a name and a closure. In our projects we tend to code up the same macro's over and over again. That's why we have put those macros in a package called laravel-collection-macros so we, and the community, can reuse them. In this post I'd like to talk a bit about a new macro that we added today called parallelMap.

parallelmap is identical to map but each item in the collection will be processed in parallel. Let's take a look at an example:

$pageSources = collect($urls)->parallelMap(function($url) {
    return file_get_contents($url);
});

The content of the given $urls will be fetched at the same time. This will be much faster that fetching the content of one url after the other. Cool stuff!

Here's another piece of code taken from our tests:

/** @test */
public function it_can_perform_async_map_operations()
{
    $this->startStopWatch();

    $collection = Collection::make([1, 2, 3, 4, 5])->parallelMap(function (int $number) {
        sleep(1);

        return $number * 10;
    });

    $this->assertTookLessThanSeconds(2);

    $this->assertEquals([10, 20, 30, 40, 50], $collection->toArray());
}

You're probably wondering how this magic works. Well, the hard part is done inside Amp's new package called parallel-functions. Here's a short description of what it does taken from their docs:

amphp/parallel-functions is a simplifying layer on top of amphp/parallel. It allows parallel code execution by leveraging threads or processes, depending on the installed extensions. All data sent to / received from the child processes / threads must be serializable using PHP’s serialize() function.

Here's an example, again take from their docs, on how you can use the package directly:

use Amp\Promise;
use function Amp\ParallelFunctions\parallelMap;

$values = Promise\wait(parallelMap([1, 2, 3], function ($time) {
    \sleep($time); // a blocking function call, might also do blocking I/O here

    return $time * $time;
}));

The parallelMap macro in our package simply uses their magic. Here's the definition of the macro:

Collection::macro('parallelMap', function (callable $callback): Collection {
    $promises = parallelMap($this->items, $callback);

    $this->items = wait($promises);

    return $this;
});

Be aware that you shouldn't use parallelMap if the work done in the closure is very simple. Using parallelMap causes quite some overhead and is memory intensive. Don't use this for small operations or on a large collection.

Thanks Niklas Keller for Amp and that wonderful amphp/parallel-functions.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "An async map function"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.