Сергей Шамбир, Адаптация Promise/A+ для взаимодействия...

46
Адаптация Promise/A+ для взаимодействия C+ + и JavaScript Сергей Шамбир Ведущий программист iSpring Solutions

Transcript of Сергей Шамбир, Адаптация Promise/A+ для взаимодействия...

Page 1: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Адаптация Promise/A+ для взаимодействия

C++ и JavaScriptСергей Шамбир

Ведущий программистiSpring Solutions

Page 2: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Дилемма метапрограммирования• Плюсы:• Вносит в язык новые возможности• Делает язык выразительнее

• Минусы:• Имеет намного больший (чем ООП) порог вхождения• Отнимает очень много (сколько угодно) времени

• Плохо подходит для решения повседневных задач на C++• Хорошо подходит, чтобы заложить фундамент новых проектов

Page 3: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Суть нашей проблемы

CEF3 render processCEF3 browser process

V8 (Javascript)

Blink (HTML/CSS)

libcef.dll libcef.dll

JSON-подобные сообщения(protobuf)

Прикладной протокол (События, запуск фоновых задач, управление жизненным циклом UI)

???Прикладной движок на C++

Прикладной UIна HTML5/CSS3

+ Javascript

Page 4: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Диспетчеризация сообщений

string message_name = request;if (message_name == kFileOpenMessageName) { dialog_state_->mode_ = FILE_DIALOG_OPEN; title = "My Open Dialog";} else if (message_name == kFileOpenMultipleMessageName) { dialog_state_->mode_ = FILE_DIALOG_OPEN_MULTIPLE; title = "My Open Multiple Dialog";} else if (message_name == kFileOpenFolderMessageName) { dialog_state_->mode_ = FILE_DIALOG_OPEN_FOLDER; title = "My Open Folder Dialog";} /* ... */

• Наивный подход из разряда «Попробуй Смержить»• Начиная с C++11 легко заменяется на map<string, function>

Page 5: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

class CClientPaymentApi{public:

// Запуск операций: возвращает обещание результатаfuture<bool> StartPayment(int itemId);future<bool> CompletePayment(int itemId);future<bool> CancelPayment(int itemId);

// Сигналы-слоты: соединение "один ко многим"

Connection DoOnConnectionFailed(const Slot<void()> &handler);};

Доменная модель API в приложении• С такой моделью мы предпочли бы работать вместо switch/case

Page 6: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Запрос операции похож на вызов функции• Операции могут завершиться успешно, с ошибкой либо быть

отменены• Операция может выполниться немедленно или отложенно

Прикладной UIна HTML5/CSS3

+ Javascript

Прикладной движок на C++

OpenDocument("cbook.doc")

returns true

Прикладной движок на C++

Прикладной UIна HTML5/CSS3

+ Javascript

OpenDocument(42)

throws TypeError

Прикладной движок на C++

Прикладной UIна HTML5/CSS3

+ Javascript

OpenDocument("1GB.doc")

cancel that

Page 7: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Сложности работы с потоками

• Блокировать UI-потоки нельзя – это заденет пользователя• Первый UI поток – browser-процессе (с прикладным C++-кодом)• Второй UI поток – в render-процессе (с прикладным Javascript-кодом)

UI-поток в browser процессе

execute task

handle event

handle event

handle event

post task

UI-поток в render процессе

IPC IPC

Page 8: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Тонкости маршалинга вызовов• Можно ли проверить типы аргументов лучше, чем через assert?• Повторять проверки в прикладном коде нелепо• Информация о типах уже есть в сигнатуре функции-колбека

• Как сериализовать исключение?• Тип или код ошибки могут подсказать стратегию обработки исключения

Page 9: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Мы решили писать шаблоны и велосипеды

template <class TValue>

class IPromise

class

CAbstr

actJav

ascrip

tBinde

r

: publ

ic IJa

vascri

ptBind

er

, publ

ic

std::e

nable_

shared

_from_

this<C

Abstra

ctJava

s

criptB

inder>

templa

te <cl

ass Re

turnTy

pe, cl

ass Cl

assTyp

e, boo

l

AddCon

st, cla

ss...

Args>

struct

WeakIn

vokerhttps://github.com/sergey-shambir/cpp-promise-demo/

Page 10: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Преимущества Promise в Javascript• Есть проверенная в деле спецификация: promisesaplus.com• “An open standard for sound, interoperable JavaScript promises—by

implementers, for implementers.”

• Есть then/catch, т.е. можно повесить callback или продолжение• Callback вызывается с чистым стеком на определённом потоке

(т.е. как новый task)

PendingFulfilled

Rejected

Задача запущена Выполнено

Page 11: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Подход «конвейер подзадач» с Promise

function loadGameMap() { let contentPromise = utils.loadUrlAsStringAsync("/res/level1.tmx"); let xmlPromise = contentPromise.then((content) => { return utils.parseXmlString(content) }); let mapPromise = xmlPromise.then((xmlDocument) => { return utils.buildGameMap(xmlDocument) }); return mapPromise;};

Запуск

FulfilledRejected

Чтение файла Разбор XML Построение карты уровня

Page 12: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Подход «у меня есть план B» с Promise

function loadUserPhotos(userId) { let netPromise = netClient.loadPhotoCollectionAsync(userId); let photosPromise = netPromise.catch(() => { return localClient.loadCachedPhotoCollection(userId); }); return photosPromise;}

Запуск Запрос к сети Запрос к оффлайн-кешу

Fulfilled Rejected

Page 13: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Подход «подождать любого» с Promise

function loadUserPhotos(userId) { let netPromise = netClient.loadPhotoCollection(userId); let localPromise = localClient.loadCachedPhotoCollection(userId); return Promise.race([netPromise, localPromise]);}

Запуск

Запрос к сети Запрос к оффлайн-кешу

Fulfilled Rejected

Page 14: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Подход «подождать всех» с Promise

function loadUserPhotos(userId) { let netPromise = netClient.loadPhotoCollection(userId); let localPromise = localClient.loadCachedPhotoCollection(userId); return Promise.all([netPromise, localPromise]);}

Запуск

Запрос к сети Запрос к оффлайн-кешу

Fulfilled RejectedЖдём 2-х

Page 15: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Недостатки Promise в Javascript• Легко нарушить контракт «Promise в конце операции переходит в

состояние Fulfilled или Rejected»• Достаточно потерять колбеки в конструкторе Promise

• Легко нарушить контракт «Promise при успешном завершении возвращает значимый результат»• Просто сделайте обработчик catch такой же, как в примерах: https://

goo.gl/dEvi8V

var p1 = new Promise(function (resolve, reject) { // .. давайте потеряем resolve/reject});

Page 16: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Основной цикл и пул потоков• В STL до сих пор нет каркаса событийного цикла• Предполагаю, что комитет не пришёл к универсальной реализации

• В Boost.Asio и в каждой ОС есть свой основной цикл• В UI-библиотеках циклы свои и в них надо встраиваться• Цикл из Boost.Asio годен для серверов, а не для UI

UI-поток

execute task

handle event

handle event

handle event

post task

Page 17: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Пул потоков на Boost.Asio в 35 строк

AsioThreadPool: https://goo.gl/NiYTUY

boost::asio::io_service

boost::asio::io_service::work

std::thread { io.run(); }

std::thread { io.run(); }

std::thread { io.run(); }

std::thread { io.run(); }

• Конструктор вызывает на каждом потоке io.run• Деструктор вызывает io.stop() и затем join потоков• Для добавления задачи вызываем io.post

Page 18: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

future в C++ и Promise в Javascript• В C++14 и C++17 future не расчитан на модель «исполнители и

задачи»• В std::future нет then• Если future получен от async, в деструкторе будет ожидание завершения

задачи

• В Concurrency TS future всё так же не расчитан на модель «исполнители и задачи»• К std::future добавляется then(callback), но нет стратегии вызова callback• Нельзя выполнить callback в предсказуемом потоке и окружении

“Why is there no std::future::then in C++17?” stackoverflow.com/questions/41310197

Page 19: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Ответ на «Use the Boost, Luke!»• Boost предоставляет then, он он имеет подводные камни• Добиться работы «как в Javascript» можно, но сложно• Даже над Boost лучше написать упрощённую и ограниченную

обёртку-велосипед• И не забудьте взять с собой макросы:

#define BOOST_THREAD_PROVIDES_EXECUTORS#define BOOST_THREAD_VERSION 4#include <boost/thread.hpp>

Page 20: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Чемпионат по отстрелу ног с Boost, раунд 1• На каком потоке по умолчанию будет вызван callback?• Ответ: на новом потоке, т.к. Launch Policy – launch::none

// .. создаём boost::promise и получаем от него futurecerr << "called then on " << this_thread::get_id() << endl;future.then([&](future<string> oldFuture) { cerr << "then callback on " << this_thread::get_id() << endl; dispatch.QuitMainLoop();});

Page 21: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Чемпионат по отстрелу ног с Boost, раунд 2• Будет ли вызван callback?• Ответ: если задача ещё не завершилась, то не будет, т.к.

возвращённый от then объект future разрушается сразу после выполнения инструкции• Уточнение: если future получен от async, всё сложно.

// .. создаём boost::promise и получаем от него futurecerr << "called then on " << this_thread::get_id() << endl;future.then(launch::deferred, [&](future<string> oldFuture) { cerr << "then callback on " << this_thread::get_id() << endl; dispatch.QuitMainLoop();});

Page 22: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Чемпионат по отстрелу ног с Boost, раунд 3• Будет ли вызван callback, если просто заменить Launch Policy?• Ответ: если у future не указан executor, будет assert• Assertion failed: this->future_->get_executor(), file c:\...\boost\

thread\future.hpp, line 4761

// .. создаём boost::promise и получаем от него futurecerr << "called then on " << this_thread::get_id() << endl;future.then(launch::executor, [&](future<string> oldFuture) { cerr << "then callback on " << this_thread::get_id() << endl; dispatch.QuitMainLoop();});

Page 23: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Чемпионат по отстрелу ног с Boost, раунд 4• Будет ли вызван callback, если установить executor, который

постит задачу в UI thread, и then вызывается из UI thread?• Ответ: нет, wait() заблокирует обработку событий в UI thread

cerr << "called then on " << this_thread::get_id() << endl;auto f2 = future.then(launch::executor, [&](future<string> oldFuture) { cerr << "then callback on " << this_thread::get_id() << endl; dispatch.QuitMainLoop();});f2.wait();

Page 24: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Безопасный callback, связанный с объектом

void WelcomeController::OnLogin(){ auto callback = std::bind(&WelcomeController::SaveLoginData, this, _1); m_api.Login(m_view.GetEmail(), m_view.GetPassword(), callback);}

• В момент вызова callback объект уже может быть уничтожен• Из документации Boost.Signals: вызов слота может происходить после

disconnect, если disconnect был сделан в другом потоке

• Решения есть• Weak this (аналог weak self в Objective-C)• Monitor (альтернатива weak this)

Page 25: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Идиома “weak this”

BindWeakPtr: goo.gl/xlRM3E

• Нужно наследовать класс от enable_shared_from_this• Нельзя вызывать shared_from_this в конструкторе и деструкторе• Не работает с std::bind

std::weak_ptr<WelcomeController> weakThis = shared_from_this();m_api.Login(m_view.GetEmail(), m_view.GetPassword(), [weakThis](const auto &data) { if (auto strongThis = weakThis.lock()) { strongThis->SaveLoginData(data); }});

Page 26: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

BindWeakPtr – адаптер std::bind

BindWeakPtr: goo.gl/xlRM3E

• Функция BindWeakPtr перегружена для const и не-const методов• Внутри создаёт WeakInvoker и вызывает bind с его копией• Объект WeakInvoker хранит weak_ptr и реализует operator()

 void WelcomeController::OnLogin(){

auto callback = BindWeakPtr(&WelcomeController::SaveLoginData, shared_from_this(), _1);

m_restClient->Login(m_view->GetEmail(), m_view->GetPassword(), callback);}

Page 27: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Портируем JS Promise в C++• Добавили метод Cancel и состояние Cancelled• Для синхронизации использовали mutex• Возможно, есть способ сделать lock-free, но в наших условиях нет

ежесекундного создания тысяч объектов Promise

Pending

Fulfilled

Rejected

Задача запущена Выполнено

Cancelled

Отменено

Page 28: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Портируем JS Promise в C++template <class ValueType>class IPromise{public: using ThenFunction = function<void(ValueType)>; using CatchFunction = function<void(exception_ptr const&)>;  virtual ~IPromise() = default;  virtual void Then(const ThenFunction &onFulfilled) = 0; virtual void Catch(const CatchFunction &onRejected) = 0; virtual void Cancel() = 0;};

Page 29: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve(42); });});

promise.then((value) => alert("Succeed 1st: " + value));promise.then((value) => alert("Succeed 2nd: " + value));promise.then((value) => alert("Succeed 3rd: " + value));

• В Javascript один объект Promise позволяет вызвать then несколько раз• В C++ это невозможно для Movable-only значений• Мы решили поддерживать Movable-only, нарушив стандарт Promise/A+

Выбор: совместимость или movable-значения

Page 30: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Состояние храним в variant• Promise содержит либо ошибку, либо исключение, либо ничего• Можно использовать variant для экономии памяти на хранение• Чтобы хранить состояние целиком, добавим два теговых типа CancelState

и PendingState

struct CanceledTag {};struct PendingState {};using StorageType = boost::variant< PendingState, CanceledTag, ValueType, std::exception_ptr>;

Page 31: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Switch по типам для variantvoid Then(const ThenFunction &onFulfilled) override{ lock_guard lock(m_mutex); if (m_then) throw std::logic_error("Cannot call Then twice"); switch (m_storage.which()) { case detail::VariantIndex<StorageType, PendingState>: m_then = onFulfilled; break; case detail::VariantIndex<StorageType, ValueType>: m_then = onFulfilled; InvokeThen(); break; }}

Page 32: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Получение which index для типа в variant

namespace detail{template <class VariantType, class VariantCase>using WhichIndex = typename boost::mpl::find< typename boost::mpl::copy< typename VariantType::types, boost::mpl::back_inserter<boost::mpl::vector<>> >::type, VariantCase >::type::pos;

template <class VariantType, class VariantCase>constexpr int VariantIndex = WhichIndex<VariantType, VariantCase>::value;}

Page 33: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Постановка задачи

// args пришёл из Javascript и выглядит так:auto args = { Value(42.2), Value("add") }; ApplyVariantArguments([](const double &value, const string &operation) {

// выполняем действие над аргументами}, args);

•Есть рекурсивный вариантный тип, который по набору типов похож на JSON•Есть callable, имеющий точно указанную сигнатуру•Нужно применить аргументы к функции

Page 34: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

// Для функторов, имеющих operator()template <typename T>struct function_traits : public function_traits<decltype(&T::operator())> {};// Для указателей на функцииtemplate <typename ReturnType, typename... Args>struct function_traits<ReturnType(*)(Args...)> {

typedef std::function<ReturnType(Args...)> f_type;};// ... для методов (константных и неконстантных) ...// функция для вывода типа из параметраtemplate <typename Callable>typename function_traits<Callable>::f_type make_function(Callable callable) {

return (typename function_traits<Callable>::f_type)(callable);}

Шаг 1: function_traits•Задаёт синонимы типов параметров и результата•Принимает лямбды, указатели на свободные функции и методы•Не принимает ни std::bind, ни generic lambda•Могут быть ошибки компиляции с перегруженными функциями

Page 35: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Шаг 2: формируем tuple и вызываем apply

// Remove `const&` and other dangerous qualifiers.template <typename ...Args>using arguments_tuple = std::tuple<typename std::decay_t<Args>...>;

template <typename R, typename ...Args>R ApplyCefArgumentsImpl(const std::function<R(Args...)> &function, const CefRefPtr<CefListValue> & args){

detail::CJavascriptArgumentsAdapter adapter(args);detail::arguments_tuple<Args...> typedArgs;

 

adapter.CheckArgumentsCount(std::tuple_size<decltype(typedArgs)>::value);detail::for_each_in_tuple(typedArgs, adapter);

 return detail::apply_tuple<R>(typedArgs, function);

}

Page 36: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Шаг 3: класс JavascriptArgumentsAdapter

template <class T>using CanConvertType = is_any_of<T, bool, double, std::string, std::wstring,

nlohmann::json, CefRefPtr<CefListValue>, CefRefPtr<CefDictionaryValue>, CefRefPtr<CefBinaryValue>>;

 template<class T>void operator()(T & destination, size_t index)const{

static_assert(CanConvertType<std::decay_t<T>>{},"argument conversion is not implemented for type T,"" please use another type");

Convert(destination, index);}

void Convert(bool &destination, size_t index)const;void Convert(int &destination, size_t index)const;void Convert(double &destination, size_t index)const;void Convert(std::string &destination, size_t index)const;void Convert(std::wstring &destination, size_t index)const;void Convert(nlohmann::json &destination, size_t index)const;void Convert(CefRefPtr<CefListValue> &destination, size_t index)const;void Convert(CefRefPtr<CefDictionaryValue> &destination, size_t index)const;void Convert(CefRefPtr<CefBinaryValue> &destination, size_t index)const;

void CJavascriptArgumentsAdapter::Convert(std::string & destination, size_t index) const

{assert(int(index) <= int(INT_MAX));CheckArgument(index, VTYPE_STRING);destination = m_arguments->GetString(int(index)).ToString();

}

Page 37: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Шаг 4: заворачиваем в std::function

using RemoteCallback = std::function<CefRefPtr<CefValue>(const CefRefPtr<CefListValue> &list)>;

template <typename R, typename ...Args>CefCallback BindCefCallImpl(const std::function<R(Args...)> &function){

return [function](const CefRefPtr<CefListValue> &list) {return ConvertReturnValue(ApplyCefArgumentsImpl(function,

list));};

}

template <typename Callable>CefCallback BindCefCall(Callable && callable){

return BindRemoteCallImpl(detail::make_function(callable));}

Page 38: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Итог: интерфейс IJavascriptBinderclass IJavascriptBinder{public:

void BindOperation(const string &name, const NativeFn &fn) = 0;void BindAsyncOperation(const string &name, const PromiseFn &fn) =

0;

template<class ...TArgs>ICefValuePromisePtr CallJavascript(const string &fn, TArgs&&...

args){

const string code = detail::FormatJsArgs(forward<TArgs>(args)...); 

return CallJsImpl(functionName, jsCode);}

protected:ICefValuePromisePtr CallJsImpl(const string &fn, const string

&code) = 0;};

Page 39: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Итог: интерфейс на стороне Javascript

cef.CefClient = goog.defineClass(null, { call: function(name, ...args) { var promise = new CancelablePromise(); var requestId = this._connector.sendRequest( name, args, promise.resolveFunc(), promise.rejectFunc()); return promise; }, addHandler: function(name, handler, object) { this._handlers[name] = handler.bind(object); }, removeHandler: function(name) { delete this._handlers[name]; },}

Page 40: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Самое время спросить о чём-нибудь...

Page 41: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Тестирование с Boost.Test•Вдохновлялись Javascript-библиотекой sinon.js• Сделали proxy-объекты

•Тесты в отдельном потоке, чтобы не блокировать Event Loop• Поток тестов ждал значение через std::future

•Проверили передачу всех типов данных и исключений• Были проблемы с передачей Object и временем жизни• Нельзя передавать тип Function• Нельзя передать три значения типа double: NaN, +INF и -INF

Page 42: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Тестирование с Boost.Test

Ожидание std::future

Поток, запустивший unit_test_main

execute task

handle event

handle event

handle event

UI-поток в browser process

Вызов Javascript через Proxy

execute task

Proxy получил значение

Page 43: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Идиома “monitor”// Нюанс: не соблюдается rule of five,// что влечёт неверное копирование monitorstruct Student {

std::shared_ptr<void> monitor;std::string name;

Student() : monitor(this, ignore) {}decltype(auto) GetNamePrinter() {

std::weak_ptr<void> monitor = this->monitor;return [=]() {

if (!monitor.expired()) {// working with this

}};

}};

Page 44: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Сторонние библиотеки для Promise/A+• tored/qml-promise – однопоточные Promise для C++/QML• rhashimoto/poolqueue – запускает Promise поверх пула потоков

или на базе таймера• grantila/q – крупная библиотека со своими 🚲 Promise, thread

pool, timers и т.п.• 0of/Promise2 – содержит заготовку Promise, интегрировать запуск

задач Promise в свой EventLoop/ThreadPool придётся самостоятельно

Page 45: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

“libdispatch” от AppleБиблиотека содержит примитивы для событийной многозадачности: очереди задач, исполнители, пул потоков и основной поток• Версия libdispatch от Apple: https://

github.com/apple/swift-corelibs-libdispatch• Версия с улучшением поддержки Linux (встраивание в event loop):

https://github.com/nickhutchinson/libdispatch• Версия с поддержкой Win32: https://github.com/DrPizza/libdispatch

Page 46: Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript

Вредные советы документацииИногда документация содержит вредные советы.• Примеры для JS Promise в сети содержат неправильную

обработку исключений (нет перевыброса): https://goo.gl/dEvi8V

Иногда документация неоднозначна (пример из STL от Microsoft):

void pop(){

// erase element at endc.pop_front();

}