Skip to content

Instantly share code, notes, and snippets.

@kelunik
Last active May 17, 2016 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kelunik/9bd92e23c4b734b457c610acf7393b98 to your computer and use it in GitHub Desktop.
Save kelunik/9bd92e23c4b734b457c610acf7393b98 to your computer and use it in GitHub Desktop.

Async\Awaitable

The goal of this specification is to define an Awaitable interface for interoperability in PHP.

An Awaitable represents the eventual result of an asynchronous operation. The primary way of interacting with an Awaitable is through its when method, which registers a callback to receive either an Awaitable's eventual value or the reason why the Awaitable has failed. They're basically the same as a Promise in JavaScript's Promises/A+ specification, but the interaction with them is different as the following paragraphs show.

This specification defines an interoperable Awaitable interface for PHP, focussing on its when method. This is required for interoperable co-routines, which can be implemented in PHP using generators. Further methods like a watch method for progress updates are out of scope of this specification. The name Awaitable works well if PHP is extended later to support await and is also alraedy used in HHVM.

Awaitable is the fundamental primitive in asynchronous programming. It should be as lightweight as possible, as any cost adds up significantly. The existing Promises/A+ specification conflicts with the goal of lightweight primitives.

Co-routines should be the preferred way of writing asynchronous code, as it allows the use of try / catch and doesn't result in a so-called callback hell. Co-routines do not use the returned Promise of a then callback, but returning a Promise is required by Promises/A+. This means a lot of useless object creations, which aren't cheap and add up.

Existing libraries using Promises/A+ can easily additionally implement Awaitable to be interoperable, while still supporting their current chainability using then.

This specification does not deal with how to create, succeed or fail Awaitables, as only the consumption of Awaitables is required to be interoperable.

Terminology

    1. Awaitable is an object implementing Interop\Async\Awaitable and conforming to this specification.
    1. Value is any legal PHP value (including null), except an instance of Interop\Async\Awaitable.
    1. Error is any value that can be thrown using the throw statement.
    1. Reason is an error indicating why an Awaitable has failed.

States

An Awaitable MUST be in one of three states: pending, succeeded, failed.

A promise in … state  
pending
  • MAY transition to either the succeeded or failed state.
succeeded
  • MUST NOT transition to any other state.
  • MUST have a value which MUST NOT change.*
failed
  • MUST NOT transition to any other state.
  • MUST have a reason which MUST NOT change.*
  • Must not change refers to the reference being immutable in case of an object, not the object itself being immutable.

Consumption

An Awaitable MUST implement Interop\Async\Awaitable and thus provide a when method to access its current or eventual value or reason.

<?php

namespace Interop\Async\Awaitable;

interface Awaitable
{
    /** @return void */
    function when(callable $callback);
}

The when method MUST accept at least one argument:

$callback – A callable conforming to the following signature:

function($error, $value) { /* ... */ }

Any implementation MUST at least provide these two parameters. The implementation MAY extend the Awaitable interface with additional parameters passed to the callback. Further arguments to when MUST have default values, so when can always be called with only one argument. Callbacks MAY decide to consume only one or none of the provided parameters. when MUST NOT return a value.

NOTE: The signature doesn't specify a type for $error. This is due to the new Throwable interface introduced in PHP 7. As this specification is PHP 5 compatible, we can't use neither Throwable nor Exception.

All registered callbacks MUST be executed in the order they were registered. If one of the callbacks throws an Exception or Throwable, it MUST be rethrown in a callable passed to Loop::defer so Loop::onError can be properly invoked by the loop. Loop refers to the global event loop accessor. The Awaitable implementation MUST then continue to call the remaining callbacks with the original reason.

When an Awaitable is resolved with another Awaitable, the Awaitable MUST keep in pending state until the passed Awaitable is resolved. Thus, the value of an Awaitable can never be another Awaitable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment