23

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.

jcoder
  • 29,554
  • 19
  • 87
  • 130
  • 1
    Not quite a duplicate, but closely related: http://stackoverflow.com/questions/14489935/implementing-futurethen-equivalent-for-asynchronous-execution-in-c11 – Christopher Creutzig Jan 07 '14 at 14:27
  • Right, not quite a duplicate but interesting anyway :) For some reason my search didn't find that one. – jcoder Jan 07 '14 at 14:29
  • 1
    "Critically it [javascript] does this in the same thread". Typically two threads are involved; (i) a thread in which a deferred is created and async handler(s) are attached, (ii) a thread in which the deferred is fulfilled or rejected and async hander(s) are executed. Given that (a) handlers need not necessarily be attached in the same thread as the deferred was created, (b) progress events may also be handled, and (c) a deferred can continue to have a life after it becomes fulfilled/rejected - then any number of further threads may also be involved. – Beetroot-Beetroot Jan 08 '14 at 02:44
  • My point is that in javascript there are threads involved, but you can guarentee that any callbacks will happen on the same thread as the main code. You can't have something searching through your array of Images at the same time as one loads and the callback decided to add it to the list... – jcoder Jan 08 '14 at 08:39
  • 2nd sentence is correct. – Beetroot-Beetroot Jan 08 '14 at 08:55
  • The first one isn't? My assumption was that javascript was essential single threaded, that all *user* code runs on the same thread. That build in functions like image loading can happen on a background thread but the notifications were delivered via the event queue on the main thread so you never really saw that threading? (Ignoring things like WebWorkers etc..) If that assumption is wrong and callbacks can happen on different threads then how is anything safe? Worried now that I've misunderstood something – jcoder Jan 08 '14 at 09:01
  • In Javascript, "single threaded" actually means "only one thread at a time". Each thread is more accurately an "event thread" - ie each thread runs in response to an environmental event. Whereas, in any given environment, there will be an initial event, there's not really such a thing as "the main code" - only an initial event and subsequent events. Asynchronous activity necessarily involves at least two event threads - one to stimulate the asnch activity, and one to respond to its completion. – Beetroot-Beetroot Jan 08 '14 at 09:38

7 Answers7

14

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.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 1
    Right, I've used Asio before. But before I knew about futures... Looks like the combination of that and boost's future does exactly what I need. Great. Thank you! – jcoder Jan 07 '14 at 14:49
  • `std::future::then` did not make it into C++17/20/23 and has presumably been dropped by the standards committee. – Chris_F Jan 22 '23 at 01:55
6

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.

xhawk18
  • 169
  • 1
  • 4
5

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • The question I have though is about the thread of the callback. If asyncLoader loads my image on a second thread and completes the future there I want the callback in the context of my original thread. (Which is why I'd need some kind of disptach and event loop in that thread). Looks like I can code something using asio to achieve what I want even if not that exact syntax . – jcoder Jan 07 '14 at 15:20
  • @jcoder agreed, having stuff on the original thread is nice. To do that, you'll want to write an augmented `future`, which involves writing an augmented `async`. Such augmented futures would allow the `then` to inject a new task onto a queue of tasks for the thread executing the future task in question. It should be doable with C++11 threading primitives; packaged task, condition variables, etc. – Yakk - Adam Nevraumont Jul 07 '15 at 14:19
2

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.

maxdev
  • 2,491
  • 1
  • 25
  • 50
  • 1
    This would call the callback in the thread that did the work though, and I'd want it to call it in the "main" thread. Part of the reason for doing this is to help with synchronization issues. – jcoder Jan 07 '14 at 14:28
  • 1
    The problem is that your main thread cant simply "wait" for another thread to finish its work.. you would have to use something like a fork-join-framework. – maxdev Jan 07 '14 at 14:30
  • This is why I mentioned that if I was to implement this I'd need the main thread to run some kind of event dispatch loop. I would expect completed futures to somehow queue themselves for dispatch.... I could write this, I was really just wondering if I could achieve the same aim in a better way without writing lots of code :) – jcoder Jan 07 '14 at 14:34
1

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

benathon
  • 7,455
  • 2
  • 41
  • 70
1

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

0

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

Alexander Vassilev
  • 1,399
  • 13
  • 23