Entwicklung & Code
Asynchrone Programmierung – Teil 4: Qt6 mit QPromise und QFuture
Das Framework Qt unterstützt mit seinem tief in der Architektur verankerten Event-System und dem Signal-Slot-Mechanismus seit jeher Entwicklerinnen und Entwickler bei der asynchronen Programmierung. Diese Möglichkeiten hat der vorangegangene Teil unserer Serie vorgestellt.
Weiterlesen nach der Anzeige

Martin Meeser ist selbständiger Diplominformatiker (Uni) und bietet Dienstleistungen zum Thema Softwareentwicklung an: Individual-Software-Entwicklung, Beratung zu Prozessen und Schulungen. In zahlreichen Projekten betreute er bisher Kunden unter anderem aus den Bereichen Automotive, Finance, Raumfahrt, Radioastronomie und Medizintechnik.
QFuture ist ein neueres Qt-Konstrukt, das das Konzept des „thennables“ – der .then()-Methoden – aus anderen Sprachen aufnimmt (z. B. Promise in JavaScript, CompletableFuture in Java oder Task in C#). Es erlaubt mit QFuture::then() das Ausführen und Hintereinanderschalten von asynchronen Funktionen, unabhängig von Threads, beispielsweise um Benutzeroberflächen asynchron zu aktualisieren.
In Qt tritt ein lesendes, konsumierendes QFuture immer als die eine Seite der gleichen Medaille auf. Auf der schreibenden, produzierenden Seite steht das QPromise. Entwicklerinnen und Entwickler können den Zustand des Promise-Future-Paares detailliert steuern: starten (QPromise::start()), aussetzen (QPromise::suspend()), beenden (QPromise::finish()) oder abbrechen (QPromise::cancel()). Schließlich können Developer nicht nur einen Rückgabewert, sondern beliebig viele setzen (QPromise::addResult()). Mit der QPromise-/QFuture-API haben sie eine praktische Schnittstelle, um Benutzer- sowie Logik-Schichten effektiv zu trennen und Fortschritte an die Bedienoberfläche zu melden.
Die Klasse QFutureWatcher liefert einen Mechanismus, um die QFuture-Funktionalität mit dem im vorangegangenen Artikel beschriebenen Signal-/Slot-Mechanismus zu verbinden. Das folgende Listing 1 zeigt ein Beispiel für die grundlegenden Funktionsweisen.
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
QFutureWatcher watcher;
QObject::connect(&watcher, &QFutureWatcher::started, []()
{
qInfo() << "future started ";
});
QObject::connect(&watcher, &QFutureWatcher::resultReadyAt, [&watcher](int i)
{
qInfo() << "result ready" << i << "=" << watcher.future().resultAt(i);
});
QObject::connect(&watcher, &QFutureWatcher::finished, [&watcher, &app]()
{
qInfo() << "future finished ";
for (int i = 0; i < watcher.future().resultCount(); ++i)
{
qInfo() << "Final result" << i << "=" << watcher.future().resultAt(i);
}
app.quit();
});
QPromise promise;
QFuture future = promise.future();
watcher.setFuture(future);
QThreadPool::globalInstance()->start([&promise]()
{
promise.start();
QThread::sleep(1);
promise.addResult(10);
QThread::sleep(1);
promise.addResult(20);
QThread::sleep(1);
promise.finish();
});
return app.exec();
}
Listing 1: Einfaches Beispiel für die Verwendung von QPromise, QFuture und QFutureWatcher.
Weiterlesen nach der Anzeige
Neben QFutureWatcher bietet die Klasse QFuture eine Fluent-API an, mit der man Futures hintereinander schaltet (das angesprochene „thennable“ bzw. auch als „chaining“ bezeichnet): Entwickler legen mit then() einen Nachfolger fest, wobei eine zweite Verwendung von then() den bisherigen Nachfolger überschreibt, sodass es immer nur einen Nachfolger geben kann. Außerdem löst die erste Verwendung von addResult des zugrunde liegenden Promise then() aus, und nicht etwa promise.finish(). Ein Abbruch des Promise mit cancel() aktiviert den Callback onCancelled (siehe Listing 2 unten).
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
QPromise promise;
QFuture future = promise.future();
QThreadPool::globalInstance()->start([&promise]()
{
promise.start();
qInfo() << QDateTime::currentDateTime().toString(Qt::ISODateWithMs) << QThread::currentThreadId() << "started";
QThread::sleep(1);
promise.addResult(1); // triggert then()
QThread::sleep(1);
// promise.cancel(); // triggert onCanceled()
promise.addResult(2); // kein Effekt auf then()
promise.finish();// kein Effekt auf then()
});
future
.onCanceled([]{/*...*/ return -1;})
.then([](int result1)
{
qInfo() << QDateTime::currentDateTime().toString(Qt::ISODateWithMs) << QThread::currentThreadId() << "result" << result1;
});
return app.exec();
}
Listing 2: Beispiel für das Auslösen von then() und onCancelled().
Die Methode then() ist sowohl beim Parameter als auch dem Rückgabewert generisch. Das bedeutet konkret: Bei QFuture hat das then-Callable einen Parameter vom Typ T. Der Rückgabetyp, den der Entwickler innerhalb des then-Callable verwendet, ist der Rückgabetyp des Futures, das wiederum mit then() in die verkettete Abfolge gehängt werden kann, siehe Listing 3:
void myFunc(QString val){/*...*/ return;}
QFuture future = // siehe Listing 12
QFuture future2 = future.then([](int result1)
{
return QString("fortytwo");
}).then(myFunc);
Listing 3: Beispiel für Future-Chaining mit then().
Falls innerhalb eines Future oder eines then()-Blocks eine Exception auftritt, wird diese an den Callback onFailed() geleitet. Dabei gilt die Regel, dass die Exception in der Kette weiter wandert, bis sie ein passender onFailed-Callback erreicht. Wenn man Programmteile in Arbeiter-Threads auslagert, muss man dort Exceptions korrekt auffangen und an promise.setException() übergeben. Nur dann funktioniert die Methode onFailed wie erwartet, siehe Listing 4:
QThreadPool::globalInstance()->start([&promise]()
{
promise.start();
try
{
throw std::runtime_error("error from f1");
promise.addResult(42); // das wird nicht erreicht
}
catch (const std::exception& ex)
{
promise.setException(std::current_exception());
}
});
future.then([](int i)
{
// ...
}).onFailed([](const std::exception& ex)
{
qInfo() << ex.what();
});
QTimer::singleShot(200, &app, &QCoreApplication::quit);
Listing 4: Korrekte Verarbeitung von Exceptions in Arbeits-Threads mit Promise.
Entwickler können auch mehrere onFailed mit einem Future verwenden. In diesem Fall wird das erste passende aufgerufen. Dabei ist die Reihenfolge relevant: Beispielsweise wird in Listing 5 das zweite onFailed nie erreicht, da im vorherigen bereits die darüber liegende Klasse abgefangen wurde.
future.then([](int res) {
// ...
throw std::runtime_error("Exception");
}).onFailed([](const std::exception &e) {
// dieses Callback wird ausgeführt
}).onFailed([](const std::runtime_error &e) {
});
Listing 5: Verwendung mehrerer onFailed-Callbacks
Innerhalb der Funktion, die onFailed verarbeitet, können Entwickler auch einen Rückgabewert angeben. Die Kette wird dann mit diesem Wert fortgeführt, siehe Listing 6:
QFuture future = ...
future
.onFailed([](const QException& ex)
{
return -1;
})
.then([](int i)
{
// im Fehlerfall ist i=-1
})
Listing 6: Fortführung der then()-Kette im Fehlerfall.
Wenn kein passender Callback onFailed vorhanden ist, wird die Exception von demjenigen Thread, in dem sie auftrat, an den aufrufenden Thread weitergeleitet und muss dort entsprechend abgefangen werden (Error Propagation), siehe Listing 7:
auto resultFuture = future.then([](int res) {
...
throw Error("message");
...
}).onFailed([](const std::exception &e) {
// wird nicht aufgerufen
}).onFailed([](const QException &e) {
// wird nicht aufgerufen
});
try {
auto result = resultFuture.result();
} catch(Error er) {
// dieser Teil wird aufgerufen
}
Listing 7: Propagation einer Exception.
Die bisher gezeigte Verwendung von then() führt Folgefunktionen standardmäßig in dem Thread aus, in dem das ursprüngliche QFuture lief. Entwickler können aber auch mit einem Parameter den Thread für die Folgefunktion bestimmen. Übergeben sie als Parameter ein QObject, wird dessen zugeordneter QThread verwendet. Bei einen QThreadPoolkommt dieser zum Einsatz.
Schließlich können Developer auch ein Argument vom Typ QtFuture::Launch übergeben: Die weitere Bestimmung Sync entspricht dem beschriebenen Standardfall (gleicher Thread wie Aufrufer), während Async automatisch den globalen Threadpool verwendet und Inherit die Einstellung des Vorgängers, siehe Listing 8:
// Standardfall, kein Parameter, entspricht Sync)
auto f1 = base.then([](int i) { ... });
auto f2 = f1.then(QtFuture::Launch::Sync, [](int i) { ... });
// QObject => Ausführung im Thread des QObjects
auto f3 = f1.then(&obj, [](int i) { ... });
// QThreadPool => Ausführung im angegebenen Pool
auto f4 = f2.then(&customPool, [](int i) { ... });
// Launch::Async => globaler Threadpool
auto f5 = f4.then(QtFuture::Launch::Async, [](int i) { ... });
// Launch::Inherit: erbt Thread des Vorgängers
auto f6 = f5.then(QtFuture::Launch::Inherit, [](int i) { … });
Listing 8: Bestimmung des Threads mit then(…)
Der Namespace QtFuture enthält darüber hinaus einige nützliche Hilfsfunktionen für Futures. Mit den Funktionen QtFuture::makeReady… erstellen Entwicklerinnen und Entwickler ein Future, das bereits im Status beendet ist (QFuture.isFinished() liefert true). makeReadyVoidFuture erzeugt ein beendetes Future ohne Wert (QFuture), makeReadyValueFuture(T) entsprechend ein QFuture und makeReadyRangeFuture ein beendetes QFuture, das mehrere Ergebnisse besitzt. makeExceptionalFuture schließlich erstellt ein beendetes QFuture, das eine Exception beinhaltet.
Diese Funktionen sind nützlich, wenn man bei einer Funktion anhand von Parametern überprüfen möchte, ob eine Bearbeitung als QFuture überhaupt möglich oder sinnvoll ist. Sofern dies nicht der Fall ist, gibt die Funktion direkt ein beendetes QFuture zurück, siehe Listing 9:
QFuture createIntFuture(int a)
{
if (a <= 0)
{
return QtFuture::makeReadyValueFuture(0);
}
QPromise promise;
QFuture result = promise.future();
QThreadPool::globalInstance()->start([promise = std::move(promise), a]() mutable
{
promise.start();
QThread::sleep(500);
promise.addResult(a + 1);
promise.finish();
});
}
Listing 9: Verwenden eines bei der Erstellung beendeten Futures mit makeReadyValueFuture.
Eine weitere nützliche Funktion ist QtFuture::whenAll(), die ein QFuture erzeugt, das beendet wird, wenn alle als Parameter übergebenen QFuture beendet sind. Die QFuture können verschiedene Template-Typen haben, dementsprechend ist der Rückgabetyp von whenAll ein QFuture mit einer Liste vom Typ std::variant.
Developer müssen hier noch auf die Besonderheit achten, dass beim Chaining mit then() die Nachfolgefunktion auf dem Thread läuft, dessen Future als Letztes beendet wurde. Dies kann, wie bereits beschrieben, ein Parameter für then() explizit festlegen, siehe Listing 10:
QFuture f_int = createIntFuture();
QFuture f_qstring = createStringFuture();
QFuture f_void = createVoidFuture();
using MyFuturesVariant = std::variant, QFuture, QFuture>;
QFuture> f_whenAll = QtFuture::whenAll(f_int, f_qstring, f_void);
f_whenAll.then(/*QtFuture::Launch::Async*/, [](const
QList& results)
{
...
});
Listing 10: Beispiel für QtFuture::whenAll(…)
QtFuture::whenAny() erzeugt ein QFuture, das beendet wird, sobald eines der übergebenen Futures fertig ist. Beim Verketten gilt, dass die Nachfolgefunktion im Thread des zuerst beendeten Futures ausgeführt wird, sofern kein expliziter Launch-Parameter ein anderes bestimmt, siehe Listing 11:
QFuture f_whenAny = QtFuture::whenAny(f_int, f_qstring, f_void);
f_whenAny.then([](const MyFuturesVariant& f)
{
});
Listing 11: Beispiel für QtFuture::whenAny(…)
Schließlich bietet die Funktion QtFuture::connect() die Möglichkeit, aus einem beliebigen Signal ein QFuture zu erzeugen, siehe Listing 12:
QTimer timer(&app);
timer.setInterval(1000);
timer.setSingleShot(true);
timer.start();
QFuture timerTimeoutFuture = QtFuture::connect(&timer, &QTimer::timeout);
timerTimeoutFuture.then([]
{
qDebug() << "QTimer timeout captured via QtFuture::connect";
});
Listing 12: Beispielhafte Verwendung von QtFuture::connect()
Entwicklung & Code
Angular 22 legt neuen Fokus auf KI-Coding
Das von Google entwickelte Webframework Angular hat die Hauptversion 22 erreicht. Signal Forms und Angular Aria sowie weitere Features sind nun produktionsreif, und das Angular-Team legt Wert darauf, Angular für den Einsatz mit KI-gestützten Entwicklungstools zu stärken. Dafür sind einige Updates und neue Features mit an Bord.
Weiterlesen nach der Anzeige
Reif für die Produktion
Das Feature Signal Forms, im Herbst letzten Jahres in Angular 21 noch experimentell, hat den produktiven Status erreicht. Dabei handelt es sich um eine Bibliothek zum Verwalten von Form-State auf Basis der reaktiven Angular Signals, mitsamt Typsicherheit beim Zugriff auf Formularfelder und zentraler, Schema-basierter Validationslogik. Seit dem letzten Release sind unter anderem eine komplette Dokumentation sowie Support für Angular Material und Angular Aria hinzugekommen.
(Bild: jaboy / 123rf.com)

Tools und Trends in der JavaScript-Welt: Die enterJS 2026 wird am 16. und 17. Juni in Mannheim stattfinden. Das Programm dreht sich in über 30 Vorträgen rund um das JavaScript-Ökosystem im Enterprise-Umfeld. Ein Angular-Workshop am 15. Juni taucht tief in Signals ein.
Angular Aria ist im neuen Release ebenfalls für den Einsatz in der Produktion geeignet. Die Bibliothek mit Fokus auf Accessibility bietet inzwischen zwölf UI-Pattern, die gängige Barrierefreiheitsaspekte abdecken. Seit Angular 21 neu hinzugekommen sind die vier Pattern Autocomplete, Select, Multiselect und Menubar. Darüber hinaus sind auch die beiden APIs resource und httpResource für asynchrone Reaktivität produktionsreif.
Angular und KI: MCP-Update und neue Agent-Skills
Das Angular-Team hat an einigen Stellschrauben gedreht, um die KI-gestützte Entwicklung mit Angular zu verbessern. So hat der experimentelle Angular-MCP-Server (Model Context Protocol) ein Update erhalten und bietet nun neue Tools, um während des KI-gestützten Erstellens von Anwendungen direkt mit dem Entwicklungsserver zu interagieren. Beispielsweise lässt sich dieser starten (devserver.start) oder stoppen (devserver.stop).
Weiterlesen nach der Anzeige
Zwei entwicklerbezogene Agent-Skills stehen ebenfalls bereit: angular-developer und angular-new-app. Der erste Skill stattet Modelle mit Best-Practices und Richtlinien für das Schreiben moderner Angular-Anwendungen aus, inklusive neuer Features wie Angular Aria und Signal Forms. Der zweite Skill richtet sich an Entwicklerinnen und Entwickler, die Angular zum ersten Mal in einer agentischen Umgebung ausprobieren wollen. Er leitet KI-Assistenten durch die Konfiguration einer lokalen Umgebung für die Angular-Entwicklung.
Alle weiteren Informationen zum neuen Release finden sich im Angular-Blog.
(mai)
Entwicklung & Code
Google: Neues KI-Modell läuft auch auf Laptops mit nur 16GB RAM
Google DeepMind hat mit Gemma 4 12B ein neues offenes KI-Modell vorgestellt, das multimodale Agenten direkt auf handelsüblichen Notebooks ermöglichen soll. Das Modell mit 12 Milliarden Parametern verarbeitet Text, Bilder und als erstes Modell dieser Größe auch Audio nativ – und benötigt dafür lediglich 16 GByte Arbeits- oder Grafikspeicher. Veröffentlicht unter der Apache-2.0-Lizenz steht es Entwicklern und Unternehmen frei zur Verfügung.
Weiterlesen nach der Anzeige
Damit senkt Google die Einstiegshürde für seine lokale KI-Agenten. Während Googles eigene On-Device-KI Gemini Intelligence auf Android-Smartphones hohe Hardwareanforderungen stellt zielt Gemma 4 12B bewusst auf die breite Masse.
Architektur ohne separate Encoder
Eine zweite Stärke des Modells liegt in seiner vereinheitlichten Architektur. Wie Google in seinem Blog erläutert, verzichtet Gemma 4 12B vollständig auf separate Vision- und Audio-Encoder. Herkömmliche multimodale Modelle von Google nutzen typischerweise eigene Encoder-Module, die Bilder und Audiodaten erst übersetzen, bevor das Sprachmodell sie verarbeitet. Gemma 4 12B geht einen anderen Weg: Hier soll der Input direkt vom LLM-Backbone verarbeitet werden.
Leistung nahe am doppelt so großen Modell
Innerhalb der Gemma-4-Familie positioniert Google das 12B-Modell zwischen den Edge-Varianten E4B, die für Smartphones und IoT-Geräte wie Raspberry Pi konzipiert sind, und dem größeren 26B-Mixture-of-Experts-Modell (MoE). In Benchmarks soll es laut Google jedoch nur knapp hinter dem stärkeren Modell zurückliegen. Ohne dedizierte GPU verlängern sich die Inferenzzeiten aber wahrscheinlich.
Wie das neue Modell im Vergleich zu 16GB-Varianten von anderen Anbietern abschneidet, ist noch nicht abzusehen.
Weiterlesen nach der Anzeige
(rie)
Entwicklung & Code
Kommentar: Microsofts Open-Source-Liebe hat einen Preis
Wie sich die Zeiten doch ändern: Ausgerechnet Microsoft liefert heute Open Source frei Haus. Richtig praktische und wirklich offene Werkzeuge für Entwickler reihte der Konzern auf der Build 2026 auf – ein quelloffenes KI-Terminal, hilfreiche Dev Configs, dazu WSL-Container und mehr als 75 Unix-Werkzeuge der Coreutils. Doch wer darin nur Entwicklerfreundlichkeit sieht, verpasst den eigentlichen Coup. Open Source ist inzwischen das perfekte Business für den Konzern.
Weiterlesen nach der Anzeige

Moritz Förster schreibt seit 2012 für die iX und heise online. Er betreut neben dem iX-Channel den Bereich Arbeitsplatz.
Microsoft ging gegen offene Standards lange strategisch vor – und die Strategie trug in den 90ern ein berüchtigtes Kürzel: „EEE – Embrace, Extend, and Extinguish“. Das US-Justizministerium dokumentierte, wie der Konzern dabei vorging. Erst drang Microsoft in Märkte mit offenen Standards ein, dann erweiterte es diese Standards um proprietäre Funktionen, und schließlich nutzte es die so entstandenen Unterschiede, um die Konkurrenz auszuschalten.
Sofort wurde das zu Microsofts Geschäftspraxis: Zum Beispiel setzt Microsoft im Browserkrieg den Internet Explorer nicht nur offensiv gegen Netscape ein; interne Memos zeigen darüber hinaus, dass der Konzern Office und HTML-Funktionen so verzahnen wollte, dass sie als Hebel ausschließlich das eigene Ökosystem stärken.
Diesmal fehlt das dritte E
Doch die Ankündigungen der Build 2026 sind nicht einfach nur EEE 2.0 mit besserem Marketing. Das Umarmen war immer nur Mittel zum Zweck, und der Zweck hieß ersticken. Heute fehlt diese dritte Stufe.
Microsoft gibt bei den Werkzeugen, die Entwickler täglich nutzen, keinen Zentimeter Boden auf. Niemand muss migrieren oder sich umgewöhnen. Und niemand lockt den offenen Standard in eine proprietäre Sackgasse. Was hier an Reibung verschwindet, kostet Microsoft nur Entwicklungsaufwand – aber keinesfalls Marktmacht.
Nehmen wir als Beispiel die Datenbanken: Statt einer hauseigenen, geschlossenen Datenbank setzt Microsoft mit Azure HorizonDB auf das offene PostgreSQL. Die Botschaft: Bleibt bei Postgres, wir bauen die KI-Bausteine drumherum. Ausgerechnet der Konzern, der mit proprietären Datenbanken groß wurde, setzt jetzt auf die Open-Source-Alternative.
Weiterlesen nach der Anzeige
Die Motivation ist betriebswirtschaftlich, Satya Nadella hat das nie verheimlicht. Seit 2014 verschiebt Microsoft sein Kerngeschäft von der Lizenzsoftware zu den Plattformdiensten, und Nadella sagt offen, dass sich mit geschlossenen Lizenzen auf Dauer kein Staat mehr machen lässt. Warum eine Lizenz einmal verkaufen, wenn man dieselbe Leistung monatlich vermieten kann? Dass heute über die Hälfte der Azure-Workloads unter Linux laufen, gehört inzwischen zum Geschäftsmodell.
Microsoft kassiert trotzdem ab
Der Coup steckt im Business-Schlachtfeld selbst. Der Kampf heißt nicht mehr Windows gegen Open Source. Er heißt Cloud gegen Cloud. Die Maut kassiert Microsoft nicht mehr an der Systemgrenze, sondern beim Cloud-Dienst: bei Azure, bei GitHub, beim Copilot-Abo. Der Konzern stellt das Kassenhäuschen einfach woanders auf.
Selbst Steve Ballmer hat das inzwischen eingeräumt. Seine frühere Haltung sei für ihre Zeit richtig gewesen, doch die Linux-Bedrohung liege heute „im Rückspiegel“. Und die harte Linie habe dem Konzern damals mehr Umsatz gebracht, als ein frühes Umarmen es getan hätte. Erst kämpfen, dann kassieren, schließlich umarmen – aus Sicht der Bilanz hat sich jede Phase gelohnt.
Bleibt die Frage nach dem dritten E
Man sollte die Wende trotzdem nicht zu gutgläubig feiern. Viele Entwickler misstrauen dem freundlichen Microsoft bis heute aus gutem Grund. Als der Konzern 2021 eine Hot-Reload-Funktion aus dem quelloffenen .NET herauslösen wollte und sie erst nach lautem Protest zurückholte, blitzten die alten Reflexe wieder auf. Auch die heute „umarmten“ Werkzeuge wie GitHub, Copilot und Codespaces tragen proprietäre Schichten in sich. Jede Nutzung zentralisiert die Branche ein Stück weiter auf einen einzigen Anbieter.
Microsofts Open-Source-Zuneigung mag heute echt sein – aber sie entspringt eben nicht einem Altruismus, sondern dem nüchternen Kalkül, nicht wie einst IBM zu erstarren. Doch in fünf Jahren kann schon wieder eine andere Business-Logik gelten.
(fo)
-
Entwicklung & Codevor 3 MonatenCommunity-Protest erfolgreich: Galera bleibt Open Source in MariaDB
-
Künstliche Intelligenzvor 3 MonatenBlade‑Battery 2.0 und Flash-Charger: BYD beschleunigt Laden weiter
-
Apps & Mobile Entwicklungvor 3 MonatenMähroboter ohne Begrenzungsdraht für Gärten mit bis zu 300 m²
-
Künstliche Intelligenzvor 3 MonateniPhone Fold Leak: Apple spart sich wohl iPad‑Multitasking
-
Künstliche Intelligenzvor 3 Monaten
JBL Bar 1300MK2 im Test: Soundbar mit Dolby Atmos, starkem Bass und Akku‑Rears
-
Social Mediavor 3 MonatenVon Kennzeichnung bis Plattformpflichten: Was die EU-Regeln für Influencer Marketing bedeuten – Katy Link im AllSocial Interview
-
Künstliche Intelligenzvor 3 MonateniX-Workshop KRITIS: Zusätzliche Prüfverfahrenskompetenz für § 8a BSIG
-
Künstliche Intelligenzvor 3 MonatenOscars 2026: Was die heise‑Leser anders entschieden hätten
