jcoder
jcoder

Reputation: 30035

c++ futures/promises like javascript?

I've been writing some javascript and one of the few things I like about the environment is the way it uses promises/futures to make handlers for asynchronous events.

In C++ you have to call .get on a future and it blocks until the result of the future is available but in Javascript you can write .then(fn) and it will call the function when the result is ready. Critically it does this in the same thread as the caller at a later time so there are no thread synchronization issues to worry about, at least not the same ones as in c++.

I'm thinking in c++ something like -

auto fut = asyncImageLoader("cat.jpg");
fut.then([](Image img) { std::cout << "Image is now loaded\n" << image; });

Is there any way to achieve this in c++? Clearly it will need some kind of event queue and event loop to handle dispatching the callbacks. I could probably eventually write the code to do most of this but wanted to see if there was any way to achieve the goal easily using standard facilities.

Upvotes: 22

Views: 12347

Answers (7)

user1950383
user1950383

Reputation: 11

Use JavaScript-like Promises for C++20. It relies on C++20 coroutines, supports ES6 await/async semantics, and very importantly, it supports 'move' so you can write wrappers for frameworks like asio (e.g. because asio::ip::tcp::socket cannot be copied).

Link: https://github.com/virgil382/JSLikePromise

Upvotes: 1

Alexander Vassilev
Alexander Vassilev

Reputation: 1439

The question is a bit old, but here is a Javascript-like promise library (consist of a single header that you simply need to include) that aims to do exactly what you ask for, of course together with some sort of async I/O library to implement the actual asyncImageLoader(). https://github.com/alxvasilev/cpp-promise

Upvotes: 0

benathon
benathon

Reputation: 7633

Take a look at https://github.com/Naios/continuable . It supports Javascript style .then(). It also supports exceptions with .fail() (instead of .catch()). There is a great talk about it here https://www.youtube.com/watch?v=l6-spMA_x6g

Upvotes: 1

xhawk18
xhawk18

Reputation: 189

I don't like c++'s future, so i wrote a promise libraries as javascript here https://github.com/xhawk18/promise-cpp

/* Convert callback to a promise (Defer) */
Defer myDelay(boost::asio::io_service &io, uint64_t time_ms) {
    return newPromise([&io, time_ms](Defer &d) {
        setTimeout(io, [d](bool cancelled) {
            if (cancelled)
                d.reject();
            else
                d.resolve();
        }, time_ms);
    });
}


void testTimer(io_service &io) {

    myDelay(io, 3000).then([&] {
        printf("timer after 3000 ms!\n");
        return myDelay(io, 1000);
    }).then([&] {
        printf("timer after 1000 ms!\n");
        return myDelay(io, 2000);
    }).then([] {
        printf("timer after 2000 ms!\n");
    }).fail([] {
        printf("timer cancelled!\n");
    });
}

int main() {
    io_service io;    
    testTimer(io);   
    io.run();
    return 0;
}

compare with Javascript promise, just --

  1. Use newPromise instead of js's new Promise
  2. Use lambda instead of js function
  3. Use d.resolve instead of js's resolve
  4. Use d.reject instead of js's reject

You can resolve/reject with any type of paramters, and need not care about the troublesome of <> in c++ template.

Upvotes: 6

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275395

While then is proposed, you can implement your own infix then via the named operator technique.

Create a struct then_t {}; and a static then_t then;. Now override operator* on the left and right so that std::future<bool> *then* lambda creates a std::async that waits on the future, and passes the result to the lambda, then returns the return value of the lambda.

This requires lots of care and attention, as you have to carefully create copies to avoid dangling references, and mess around with r and l value syntax to make it fully efficient.

The end syntax you get is:

aut fut = asyncLoader("cat.jpg");
fut *then* [&](Image img) { std::cout << "Image loaded: " << img; };

which is pretty close to what you want.

If you are really smart, you could even have it also support:

aut fut = asyncLoader("cat.jpg");
fut *then* [=] { std::cout << "Image loaded: " << fut.get(); };

which gets rid of some of the boilerplate and would be useful sometimes. This requires asyncLoader to return a std::shared_future instead of a future.

Upvotes: 5

ComicSansMS
ComicSansMS

Reputation: 54589

A .then function for std::future has been proposed for the upcoming C++17 standard.

Boost's implementation of future (which is compliant with the current standard, but provides additional features as extensions) already provides parts of that functionality in newer versions (1.53 or newer).

For a more well-established solution, take a look at the Boost.Asio library, which does allow easy implementation of asynchronous control flows as provided by future.then. Asio's concept is slightly more complicated, as it requires access to a central io_service object for dispatching asynchronous callbacks and requires manual management of worker threads. But in principle this is a very good match for what you asked for.

Upvotes: 13

maxdev
maxdev

Reputation: 2566

You could pass an object thats for example implementing a Runnable class to the "then" method of the Future class. Once the Future finished its work, call the "run" method of the passed object.

Upvotes: 2

Related Questions