歡迎使用本服務!您可能不喜歡用描述多步驟的 C++ 編寫程式碼 非同步作業
fpromise::promise<>
[1]。
這樣會比較容易本指南說明非同步作業的相關常見問題
規劃流程程式設計,並提供常見的使用模式來解決這些問題
fpromise::promise<>
程式庫中的問題。
非同步程式碼的挑戰是什麼?
在 fpromise::promise<>
程式庫中,非同步工作的定義為
由多個「同步」程式碼區塊組成,具有明確暫停特性
點。
定義非同步工作時,必須具有以下項目的解決方案: 問題:
提升控制流程:同步的「序列」如何? 又是如何表示資料在不同區塊的表示方式?方法是在 能理解嗎?
狀態與資源: 執行支援工作,以及必須擷取哪些外部資源?簡介 該如何確保安全?
術語
fpromise::promise<>
是僅供移動的物件,由 lambda 或 描述非同步工作,最終會產生 值或錯誤- 處理常式函式是建立承諾時提供的回呼。
- 接續函式是提供給各種方法的回呼。 延續現有承諾。
fpromise::executor
負責排程和執行承諾。 Promise 必須在擁有者轉讓給fpromise::executor
。此時,執行程式會負責排程 和執行程序fpromise::context
是選擇性地傳遞至處理常式和接續函式,以 取得fpromise::executor
的存取權,以及低階暫停和繼續 控制項
建構與執行第一個 fpromise::promise<>
我們動個簡單的承諾
#include <lib/fpromise/promise.h>
...
fpromise::promise<> p = fpromise::make_promise([] {
// This is a handler function.
auto world_is_flat = AssessIfWorldIsFlat();
if (world_is_flat) {
return fpromise::error();
}
return fpromise::ok();
});
p
現在包含說明簡易工作的承諾。
為執行承諾,必須排定在
fpromise::executor
。最常用的執行程式是 async::Executor
[2]。
它會在 async_dispatcher_t
上排定回呼用途
另外還有fpromise::single_threaded_executor
及其使用
相關聯的方法 fpromise::run_single_threaded()
[3]。
這個名稱
// When a promise is scheduled, the `fpromise::executor` takes ownership of it.
fpromise::result<> result = fpromise::run_single_threaded(std::move(p));
assert(result.is_ok());
建構更複雜的fpromise::promise<>
傳回、錯誤類型和解析度狀態
如上所述,fpromise::promise<>
的範本引數代表
傳回和錯誤類型:
fpromise::promise<ValueType, ErrorType>
錯誤類型可省略,系統會使用預設的錯誤類型 void
(例如 fpromise::promise<MyValueType>
等於 fpromise::promise<MyValueType,
void>
)。
在執行期間,承諾產品最終必須達到下列其中一種狀態:
- 成功:處理常式函式或上次接續函式 (請見下方)
已傳回
fpromise::ok()
。 - 錯誤:已傳回處理常式函式或某些接續函式
fpromise::error()
,「而且」沒有後續的接續函式攔截了此函式。 - 已放棄:承諾在解決「成功」或 發生錯誤,
.then()
、.and_then()
、.or_else()
:鏈結非同步區塊
通常,系統會將複雜的工作拆解成更精細的任務。每個 這些工作必須以非同步方式執行 您需要保留工作之間的依附元件。可用的值包括 例如:
fpromise::promise::then()
很適合用來定義工作依附元件,例如 並將工作 1 與工作 2 搭配使用,無論工作 1 的狀態為何。先前的工作的 是透過fpromise::result<ValueType, ErrorType>&
或const fpromise::result<ValueType, ErrorType>&
類型的引數接收結果。
auto execute_task_1_then_task_2 =
fpromise::make_promise([]() -> fpromise::result<ValueType, ErrorType> {
...
}).then([](fpromise::result<ValueType, ErrorType>& result) {
if (result.is_ok()) {
...
} else { // result.is_error()
...
}
});
fpromise::promise::and_then()
只適合用來定義工作依附元件 如果工作 1 成功完成先前工作的結果ValueType&
或ValueType&
類型的引數。
auto execute_task_1_then_task_2 =
fpromise::make_promise([]() { ... }).and_then([](ValueType& success_value) {
...
});
fpromise::promise::or_else()
只能用於定義工作依附元件,方法為: 工作 1 失敗的情形先前工作的結果會透過ErrorType&
或const ErrorType&
類型的引數。
auto execute_task_1_then_task_2 =
fpromise::make_promise([]() { ... }).or_else([](ErrorType& failure_value) {
...
});
fpromise::join_promises()
和fpromise::join_promise_vector()
:同時執行
有時候,可以執行多個保證,而它們之間沒有依附元件。
但匯總結果是下一個非同步步驟的依附元件。在本
案件 (fpromise::join_promises()
和 fpromise::join_promise_vector()
) 可用於彙整
做出多項承諾
如果變數參照每項承諾值,就會用到 fpromise::join_promises()
。
fpromise::join_promises()
支援異質承諾類型。先前工作
是透過 std::tuple<...>&
或 const
std::tuple<...>&
類型的引數接收結果。
auto DoImportantThingsInParallel() {
auto promise1 = FetchStringFromDbAsync("foo");
auto promise2 = InitializeFrobinatorAsync();
return fpromise::join_promises(std::move(promise1), std::move(promise2))
.and_then([](std::tuple<fpromise::result<std::string>,
fpromise::result<Frobinator>>& results) {
return fpromise::ok(std::get<0>(results).value() +
std::get<1>(results).value().GetFrobinatorSummary());
});
}
fpromise::join_promise_vector()
若將承諾產品儲存在
std::vector<>
。這有附加的限制條件,每項承諾的承諾都是
同質(同類型)。先前工作收到的結果
並透過 std::vector<fpromise::result<ValueType, ErrorType>>&
類型的引數
或 const std::vector<fpromise::result<ValueType, ErrorType>>&
。
auto ConcatenateImportantThingsDoneInParallel() {
std::vector<fpromise::promise<std::string>> promises;
promises.push_back(FetchStringFromDbAsync("foo"));
promises.push_back(FetchStringFromDbAsync("bar"));
return fpromise::join_promise_vector(std::move(promises))
.and_then([](std::vector<fpromise::result<std::string>>& results) {
return fpromise::ok(results[0].value() + "," + results[1].value());
});
}
return fpromise::make_promise()
:傳回新的承諾來鏈結或分支
推遲該將承諾鏈結在一起的決策也許會很有用
直到執行階段為止這個方法與執行的鏈結不同
語法 (透過使用連續 .then()
、.and_then()
和
.or_else()
呼叫)。
而不是傳回 fpromise::result<...>
(使用 fpromise::ok
或 fpromise::error
),
處理常式函式可能會傳回新的承諾,而這個承諾將在
處理常式函式會傳回此值
fpromise::make_promise(...)
.then([] (fpromise::result<>& result) {
if (result.is_ok()) {
return fpromise::make_promise(...); // Do work in success case.
} else {
return fpromise::make_promise(...); // Error case.
}
});
這種模式也有助於分解長期承諾
例如讓連續函式傳回
上述範例 DoImportantThingsInParallel()
的結果。
宣告並保留中繼狀態
某些工作要求狀態只有在保證本身 是待處理或執行中這個狀態不適合移至任何 的 lambda 原因是需要共用,也適合傳輸 希望其生命週期具有長期保留狀態 這才剛好相反
雖然不是唯一的解決方案,但同時使用 std::unique_ptr<>
和
std::shared_ptr<>
是常見模式:
std::unique_ptr<>
fpromise::promise<> MakePromise() {
struct State {
int i;
};
// Create a single std::unique_ptr<> container for an instance of State and
// capture raw pointers to the state in the handler and continuations.
//
// Ownership of the underlying memory is transferred to a lambda passed to
// `.inspect()`. |state| will die when the returned promise is resolved or is
// abandoned.
auto state = std::make_unique<State>();
state->i = 0;
return fpromise::make_promise([state = state.get()] { state->i++; })
.and_then([state = state.get()] { state->i--; })
.inspect([state = std::move(state)](const fpromise::result<>&) {});
}
std::shared_ptr<>
fpromise::promise<> MakePromise() {
struct State {
int i;
};
// Rely on shared_ptr's reference counting to destroy |state| when it is safe
// to do so.
auto state = std::make_shared<State>();
state->i = 0;
return fpromise::make_promise([state] { state->i++; }).and_then([state] {
state->i--;
});
}
fpromise::scope
:捨棄承諾以避免違反記憶體安全規定
fpromise::scope
會把 fpromise::promise<>
的生命週期連結至
將資源用於記憶體中例如:
#include <lib/fpromise/scope.h>
class A {
public:
fpromise::promise<> MakePromise() {
// Capturing |this| is dangerous: the returned promise will be scheduled
// and executed in an unknown context. Use |scope_| to protect against
// possible memory safety violations.
//
// The call to `.wrap_with(scope_)` abandons the promise if |scope_| is
// destroyed. Since |scope_| and |this| share the same lifecycle, it is safe
// to capture |this|.
return fpromise::make_promise([this] {
// |foo_| is critical to the operation!
return fpromise::ok(foo_.Frobinate());
})
.wrap_with(scope_);
}
private:
Frobinator foo_;
fpromise::scope scope_;
};
void main() {
auto a = std::make_unique<A>();
auto promise = a->MakePromise();
a.reset();
// |promise| will not run any more, even if scheduled, protected access to the
// out-of-scope resources.
}
fpromise::sequencer
:在個別承諾的完成項目上封鎖承諾
TODO:您可以 .wrap_with(sequencer) 封鎖此承諾,並在 與同一個序列器物件包裝的最後一個承諾
#include <lib/fpromise/sequencer.h>
// TODO
fpromise::bridge
:整合回呼式非同步函式
TODO:fpromise::bridge 適合用於鏈結回呼式非同步的接續作業 函式
#include <lib/fpromise/bridge.h>
// TODO
fpromise::bridge
:將單一接續鏈的執行作業分離
TODO:fpromise::bridge 也可將一個接續鏈分解成兩條
保證可以在不同的 fpromise::executor
執行個體上執行
常見問題
and_then
或 or_else
序列必須包含相容的類型
使用 and_then
建構承諾時,每次連續接續作業都可能發生
不同的 ValueType,但必須具有相同的 ErrorType,因為 and_then
轉送先前的錯誤而不消耗這些錯誤。
使用 or_else
建構承諾時,每次連續接續作業都可能具有
不同的 ErrorType,但必須使用相同的 ValueType,因為 or_else
轉送之前的值,而不使用這些值。
如要在序列中間變更類型,請使用 then
使用
,並產生所需類型的新結果。
以下範例不會編譯,因為
最後一個 and_then
處理常式與前一個處理常式的結果不相容。
auto a = fpromise::make_promise([] {
// returns fpromise::result<int, void>
return fpromise::ok(4);
}).and_then([] (const int& value) {
// returns fpromise::result<float, void>
return fpromise::ok(value * 2.2f);
}).and_then([] (const float& value) {
// ERROR! Prior result had "void" error type but this handler returns const
// char*.
if (value >= 0)
return fpromise::ok(value);
return fpromise::error("bad value");
}
使用 then
取用結果並變更其類型:
auto a = fpromise::make_promise([] {
// returns fpromise::result<int, void>
return fpromise::ok(4);
}).and_then([] (const int& value) {
// returns fpromise::result<float, void>
return fpromise::ok(value * 2.2f);
}).then([] (const fpromise::result<float>& result) -> fpromise::result<float, const char*> {
if (result.is_ok() && result.value() >= 0)
return fpromise::ok(value);
return fpromise::error("bad value");
}
處理常式 / 接續函式可以傳回 fpromise::result<>
或新的 fpromise::promise<>
,不能同時傳回兩者
建議您編寫可傳回 fpromise::promise<>
的處理常式
條件式分支版本,以及另一個中的 fpromise::ok()
或 fpromise::error()
。這是
無效,因為編譯器無法將 fpromise::result<>
轉換為
fpromise::promise<>
。
解決方法是傳回可解析結果的 fpromise::promise<>
想要:
auto a = fpromise::make_promise([] {
if (condition) {
return MakeComplexPromise();
}
return fpromise::make_ok_promise(42);
});
接續簽章
你是否曾看到這類錯誤訊息?
../../sdk/lib/fit-promise/include/lib/fpromise/promise_internal.h:342:5: error: static_assert failed "The provided handler's last argument was expected to be of type V& or const V& where V is the prior result's value type and E is the prior result's error type. Please refer to the combinator's documentation for
a list of supported handler function signatures."
或:
../../sdk/lib/fit-promise/include/lib/fpromise/promise.h:288:5: error: static_assert failed due to requirement '::fpromise::internal::is_continuation<fpromise::internal::and_then_continuation<fpromise::promise_impl<fit::function_impl<16, false, fpromise::result<fuchsia::modular::storymodel::StoryModel, void> (fpromise::context &)> >, (lambda at ../../src/modular/bin/sessionmgr/story/model/ledger_story_model_storage.cc:222:17)>, void>::value' "Continuation type is invalid. A continuation is a callable object with this signature: fpromise::result<V, E>(fpromise::context&)."
這很可能表示其中一個接續函式具有特徵碼 輸入的值不同持續函式的有效簽章為 如下所示:
針對 .then()
:
.then([] (fpromise::result<V, E>& result) {});
.then([] (const fpromise::result<V, E>& result) {});
.then([] (fpromise::context& c, fpromise::result<V, E>& result) {});
.then([] (fpromise::context& c, const fpromise::result<V, E>& result) {});
針對 .and_then()
:
.and_then([] (V& success_value) {});
.and_then([] (const V& success_value) {});
.and_then([] (fpromise::context& c, V& success_value) {});
.and_then([] (fpromise::context& c, const V& success_value) {});
針對 .or_else()
:
.or_else([] (E& error_value) {});
.or_else([] (const E& error_value) {});
.or_else([] (fpromise::context& c, E& error_value) {});
.or_else([] (fpromise::context& c, const E& error_value) {});
針對 .inspect()
:
.inspect([] (fpromise::result<V, E>& result) {});
.inspect([] (const fpromise::result<V, E>& result) {});
擷取和引數生命週期
Promise 由處理常式和持續函式組成,通常為 lambdas。建構 lambda 擷取清單時必須謹慎,以免 當處理常式或執行個體繼續執行時, 問題。
例如,這個承諾會擷取保證無效的記憶體 Foo() 傳回的時間 (因此,當傳回的 Pro 為排程時, 執行)。
fpromise::promise<> Foo() {
int i;
return fpromise::make_promise([&i] {
i++; // |i| is only valid within the scope of Foo().
});
}
實際程式碼中的執行個體更複雜。較不容易理解的範例:
fpromise::promise<> Foo() {
return fpromise::make_promise(
[i = 0] { return fpromise::make_promise([&i] { i++; }); });
}
fpromise::promise
主動刪除處理常式和接續函式:
當最外層的處理常式傳回最內層的處理常式時,就會刪除該處理常式。
請參閱「宣告並保留中繼狀態」以上就是「正確」的
採用的模式
>>>要撰寫的章節
- 從一種錯誤類型轉換成另一種錯誤
- fpromise::bridge
- 常見問題: 擷取的狀態生命週期