Datadog collects and monitors your PHP app metrics and distributed traces in real-time with application performance monitoring. Decrease downtime and performance issues with Datadog APM by tracing requests across service boundaries and drilling into individual traces end-to-end with flame graphs. Start your 14-day trial for free today.

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()

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

        return $number * 10;


    $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.

Follow me on Twitter. I regularly tweet out programming tips, and what I myself have learned in ongoing projects.

Every two weeks 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.