这是另一个经典的“我希望世界是同步的”问题。你不能那样编码。该getData
方法不能按照您希望的方式编写。getData
这样做会阻塞,这非常浪费并且可能导致有趣的问题——不是最后一个是可怕的用户体验。
根据您的应用程序,将有几个可能的修复:
getData
使用协程以隐式延续传递风格重做co_yield
- 这是未来,只能在最新的编译器上完成,除非您使用诸如 boost 协程之类的黑客攻击。
以明确的延续传递风格重做getData
,
getData
当数据可用时,以懒惰的方式重做并通知,
有一个明确的状态机来处理你的代码进度。
延续传递风格需要最少的更改。还要注意其他修复 - 最值得注意的是,您不应该使用QNetworkAccessManager
' 信号:您只对这个查询的结果感兴趣,而不是每个查询!QNetworkAccessManager::finished
仅当您真正拥有一个可以处理所有或至少最频繁请求的中心点时,捕获信号才有用。在这种情况下,这是一个明智的优化:malloc
将第一个连接添加到一个没有连接的对象有几个 s 的开销。
void Class::getData(const QString &urlSuffix, std::function<void(const QString &)> cont) {
auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
QNetworkRequest request(QUrl(urlString));
auto *reply = m_manager.get(request);
QObject::connect(reply, &QNetworkReply::finished, this, [=]{
QString result;
auto data = reply->readAll();
QJsonParseError jpe;
auto jdoc = QJsonDocument::fromJson(data, &jpe);
auto const synonyms = jdoc.array();
for (auto &value : synonyms) {
auto object = value.toObject();
auto s = object.value("<VALUE">).toString();
if (!result.isEmpty())
result.append(QLatin1String(", "))
result.append(s);
}
reply->deleteLater();
cont(result);
});
}
懒惰的风格要求使用的代码getData
是可重新启动的,并且也允许继续传递,只要继续连接到信号:
class Class : public QObject {
Q_OBJECT
QString m_cachedData;
QNetworkAccessManager m_manager{this};
Q_SIGNAL void dataAvailable(const QString &);
...
};
QString Class::getData(const QString &urlSuffix) {
if (!m_cachedData.isEmpty())
return m_cachedData;
auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
QNetworkRequest request(QUrl(urlString));
auto *reply = m_manager.get(request);
QObject::connect(reply, &QNetworkReply::finished, this, [=]{
m_cachedData.clear();
auto data = reply->readAll();
QJsonParseError jpe;
auto jdoc = QJsonDocument::fromJson(data, &jpe);
auto const synonyms = jdoc.array();
for (auto &value : synonyms) {
auto object = value.toObject();
auto s = object.value("<VALUE">).toString();
if (!m_cachedData.isEmpty())
m_cachedData.append(QLatin1String(", "))
m_cachedData.append(s);
}
reply->deleteLater();
emit dataAvailable(m_cachedData);
});
return {};
}
状态机将状态进程形式化:
class Class : public QObject {
Q_OBJECT
QStateMachine m_sm{this};
QNetworkAccessManager m_manager{this};
QPointer<QNetworkReply> m_reply;
QState s_idle{&m_sm}, s_busy{&m_sm}, s_done{&m_sm};
Q_SIGNAL void to_busy();
void getData(const QString &);
...
};
Class::Class(QObject * parent) : QObject(parent) {
m_sm.setInitialState(&s_idle);
s_idle.addTransition(this, &Class::to_busy, &s_busy);
s_done.addTransition(&s_idle);
m_sm.start();
}
void Class::getData(const QString &urlSuffix) {
static char const kInit[] = "initialized";
auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
QNetworkRequest request(QUrl(urlString));
m_reply = m_manager.get(request);
s_busy.addTransition(reply, &QNetworkReply::finished, &s_done);
to_busy();
if (!s_done.property(kInit).toBool()) {
QObject::connect(&s_done, &QState::entered, this, [=]{
QString result;
auto data = m_reply->readAll();
QJsonParseError jpe;
auto jdoc = QJsonDocument::fromJson(data, &jpe);
auto const synonyms = jdoc.array();
for (auto &value : synonyms) {
auto object = value.toObject();
auto s = object.value("<VALUE">).toString();
if (!result.isEmpty())
result.append(QLatin1String(", "))
result.append(s);
}
m_reply->deleteLater();
});
s_done.setProperty(kInit, true);
}
}