像JavaScript这样的C ++期货/承诺

c++ futures/promises like javascript?

本文关键字:期货 承诺 JavaScript      更新时间:2023-10-16

我一直在编写一些JavaScript,我喜欢环境的少数几件事之一是它使用promises/futures为异步事件制作处理程序的方式。

在C++中,你必须在未来调用.get,它会阻塞,直到未来的结果可用,但在Javascript中,你可以编写.then(fn),当结果准备好时,它将调用该函数。至关重要的是,它稍后在与调用方相同的线程中执行此操作,因此无需担心线程同步问题,至少与 c++ 中的线程同步问题不同。

我在 c++ 中思考类似的东西 -

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

有没有办法在 c++ 中实现这一点?显然,它将需要某种事件队列和事件循环来处理调度回调。我可能最终可以编写代码来完成大部分工作,但想看看是否有任何方法可以使用标准设施轻松实现目标。

std::future.then函数已被提议用于即将推出的 C++17 标准。

Boost的未来实现(符合当前标准,但提供附加功能作为扩展)已经在较新版本(1.53或更高版本)中提供了部分功能。

有关更完善的解决方案,请查看 Boost.Asio 库,它确实允许轻松实现 future.then 提供的异步控制流。Asio 的概念稍微复杂一些,因为它需要访问用于调度异步回调的中央io_service对象,并且需要手动管理工作线程。但原则上,这与你所要求的非常匹配。

我不喜欢 c++ 的未来,所以我在这里写了一个 promise 库作为 javascripthttps://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;
}

与Javascript的承诺相比,只是——

  1. 使用 newPromise 而不是 js 的新 Promise
  2. 使用 lambda 而不是 js 函数
  3. 使用 d.resolve 而不是 js 的解析
  4. 使用 d.reject 而不是 js 的 reject

您可以使用任何类型的参数解析/拒绝,而不必担心 c++ 模板中<>的麻烦。

虽然提出了then,但您可以通过命名运算符技术实现自己的中缀then

创建struct then_t {};static then_t then;。 现在覆盖左右operator*,以便std::future<bool> *then* lambda创建一个等待futurestd::async,并将结果传递给lambda,然后返回lambda的返回值。

这需要大量的小心和关注,因为您必须仔细创建副本以避免悬而未决的引用,并弄乱 r 和 l 值语法以使其完全有效。

你得到的结束语法是:

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

这非常接近你想要的。

如果你真的很聪明,你甚至可以让它也支持:

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

这摆脱了一些样板,有时会很有用。 这要求asyncLoader返回std::shared_future而不是future

例如,

您可以将实现 Runnable 类的对象传递给 Future 类的 "then" 方法。一旦 Future 完成其工作,调用传递对象的 "run" 方法。

看看 https://github.com/Naios/continuable . 它支持Javascript风格的.then()。 它还支持具有.fail()(而不是.catch())的异常。 这里有一个很好的讨论 https://www.youtube.com/watch?v=l6-spMA_x6g

使用类似 JavaScript 的承诺 for C++20。 它依赖于 C++20 个协程,支持 ES6 await/async 语义,而且非常重要的是,它支持"move",因此您可以为 asio 等框架编写包装器(例如,因为 asio::ip::tcp::socket 无法复制)。

链接: https://github.com/virgil382/JSLikePromise

这个问题有点老了,但这里有一个类似 Javascript 的承诺库(由一个你只需要包含的标头组成),旨在完全按照你的要求去做,当然还有某种异步 I/O 库来实现实际的asyncImageLoader()。https://github.com/alxvasilev/cpp-promise