异步对调用者滚雪球,不能使构造函数异步
async is snowballing to callers, can't make constructor async
我有一个函数 loadData
可以从文件中加载一些文本:
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/data/entities.json');
}
loadString
方法来自Flutter SDK,是异步的
然后在另一个方法中调用 loadAsset
方法,我必须将其标记为 async
,因为 loadAsset
是异步的,我需要使用 await
:
Future<List<Entity>> loadEntities() async {
String jsonData = await loadAsset();
return parseData(jsonData);
}
parseData
方法不是异步的,它接收一个String
,解析它,return一个对象列表:
List<Entity> parseData(String jsonString) {
...
}
但由于 loadEntities
必须用 async
标记,这就要求它 return 是 Future
,但实际上,它不是 Future
因为我使用 await
,它等待 loadAsset
方法完成,然后使用结果调用 parseData
函数。
这很容易变成 async
调用的滚雪球,因为每个使用 loadEntities
的方法也必须标记为 async
。
此外,我不能在 class 构造函数中使用 loadEntities
,因为构造函数应标记为 async
,这在 Dart 中是不允许的。
我在 Dart 中使用 async/await
模式错了吗?我如何在 class 构造函数中使用 loadEntities
方法?
不,异步是会传染的,没有办法从异步返回到同步执行。
async
/await
只是 methodThatReturnsFuture().then(...)
的语法糖
用async
标记一个方法只是为了让你在它的内部使用await
。如果没有 async
,您仍然需要 return 一个 Future
以便调用代码仅在 loadAsset()
的结果可用后执行。
您可以直接使用异步调用返回的 Future。这看起来像这样:
class HasAsync {
HasAsync() {
asyncFunction().then((val) {
print(val);
});
}
Future<int> asyncFunction() async {
int val = await otherFunction();
return val;
}
}
您不能在 non-async 函数中使用 await。
因为你用 'flutter' 标记了它,我猜这是在一个 flutter 应用程序中。如果是这种情况,请查看 docs for FutureBuilder - 它可能会对您尝试做的事情有所帮助。
我知道我可能来不及使用这个答案,但我还是写了它,希望有人会觉得它有用。所以这是我的两分钱。
当我第一次尝试弄清楚什么是异步编程以及如何使用它时,我的思考过程与您相同。
既然是关于Flutter的问题,我就用dart来解释一下。
首先,让我们深入了解在异步编程中使用 async await 的基本实际目的。
根据 flutter 文档,async 和 await 关键字的目的是声明性地将函数标记为异步并使用它的结果。
- 要定义异步函数,在函数体之前添加 async
- await 关键字仅在异步函数中有效。
因此,每当您尝试从标记为异步的函数中获取输出时,它别无选择,只能 return Future。请查看以下示例以获得更多说明。
- 首先,你有一个函数可以做一些计算
- 其次,您有一个简单的函数,它通过执行一个简单的 http get 请求从 API 获取数据。
最后是另一个函数,它将处理一些数据并打印一些值。
void processInfo(){
calculateStuff();
Future<Map> decodedData = getDataFromInternet();
getDataProcessed(decodedData);
}
所以在同步编程中,这意味着所有三个函数将一个接一个地执行。但是假设第二个函数 getDataFromInternet() 是异步调用的。一个简单的实现如下所示。
Future<Map> getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
return decodedData;
}
因此 return 未来需要上述功能。问题是为什么?
这很简单。在这种情况下,这是因为我们想要 return 某些东西,并且在执行 return 语句时,来自 'get request' 或 的数据可能不可用那一刻。
Thus, the function returns a Future type result which and either be in complete state or incomplete state.
那么我们如何处理这个结果呢?事实上,这可以通过 3 种方式完成。
1.方法一 - 将其作为承诺处理
因此,一旦 getDataFromInternet() 函数 return 在此示例中成为 Future 结果,您就需要像处理 javascript 中的承诺那样处理该未来结果。请参考下面的代码示例。
void getDataProcessed(Future<Map> data) {
print('getting data from future map');
data.then((d) {
print(d);
});
}
2。方法二 - 将父函数标记为异步(传染方式)
void processInfo() async{
calculateStuff();
//now you can simply await that result and process the result rather
//than handling a Future<Map> result in this case.
//Note: it is not required to use future variable because we are awaiting
//for result
Map decodedData = await getDataFromInternet();
getDataProcessed(decodedData);
}
所以在这种情况下,getDataProcessed() 函数看起来像这样。
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
3。方法三 - 在同步函数中使用异步方法的结果(非传染方式)
在这种情况下,processInfo() 函数将略有变化,即 getDataProcessed() 将不再在此方法中调用,看起来像这样。
void processInfo(){
calculateStuff();
getDataFromInternet();
}
我们可以使用 getDataFromInternet() 函数的结果来调用 getDataProcessed(),而不是在 processInfo() 函数中调用 getDataProcessed() function.this 意味着我们不必将 processInfo() 标记为异步,我们可以在执行完 getDataFromInternet() 方法后处理 getDataProcessed() 方法。以下代码示例演示了如何执行此操作。
void getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
//in this case, since we are awaiting for get results response and the
//function is not expected to return anything the data type passed into
//getDataProcessed() function now will be of type Map rather than being type
//Future<Map> . Thus allowing it to be a synchronous function and without
//having to handle the future objects.
getDataProcessed(decodedData);
}
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
So revising back this long answer,
- async/await is just the declarative way to mark asynchronous functions
- when an asynchronous function is called it can be handled in 3 ways.
- get the return Future and handle it like a promise with the use of 'then()' function so no need to mark the parent
function async
- mark the parent function async and handle the returned object with await to force the function to wait for the result.
- call the desired function with the output of the async function at the end of the async function. This will allow the main
function to continue non-dependent functions while waiting for the
results of the async function and one the async function get the
results it can go in to the other function at the end and execute it
with the data received.
then
和await
是不同的。 await
将在那里停止程序,直到 Future
任务完成。但是 then
不会阻止该程序。 then
中的块将在 Future
任务完成后执行。
如果您希望程序等待 Future
任务,请使用 await
。如果您希望您的程序继续 运行 并且 Future
任务“在后台”执行此操作,请使用 then
.
关于你的问题,建议重新设计。在其他地方执行构造函数所需的加载资产和其他 async
事情。这些任务完成后,再调用构造函数。
最简单的构造函数是 Future()
,它接受一个函数和 return 一个与函数的 return 类型相匹配的未来。稍后函数异步运行,future 以函数的 return 值完成。这是使用 Future()
:
的示例
void main() {
final myFuture = Future(() {
print('Creating the future.'); // Prints second.
return 12;
});
print('Done with main().'); // Prints first.
}
要使用完整的值,可以使用then()
。这是每个未来的实例方法,您可以使用它来为未来完成时的值注册回调。你给它一个函数,它接受一个匹配未来类型的参数。一旦 future 以一个值完成,您的函数将被调用为该值。
void main() {
Future.delayed(
const Duration(seconds: 3),
() => 100,
).then((value) {
print('The value is $value.'); // Prints later, after 3 seconds.
});
print('Waiting for a value...'); // Prints first.
}
有关更多信息,请参阅 https://medium.com/dartlang/dart-asynchronous-programming-futures-96937f831137
我有一个函数 loadData
可以从文件中加载一些文本:
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/data/entities.json');
}
loadString
方法来自Flutter SDK,是异步的
然后在另一个方法中调用 loadAsset
方法,我必须将其标记为 async
,因为 loadAsset
是异步的,我需要使用 await
:
Future<List<Entity>> loadEntities() async {
String jsonData = await loadAsset();
return parseData(jsonData);
}
parseData
方法不是异步的,它接收一个String
,解析它,return一个对象列表:
List<Entity> parseData(String jsonString) {
...
}
但由于 loadEntities
必须用 async
标记,这就要求它 return 是 Future
,但实际上,它不是 Future
因为我使用 await
,它等待 loadAsset
方法完成,然后使用结果调用 parseData
函数。
这很容易变成 async
调用的滚雪球,因为每个使用 loadEntities
的方法也必须标记为 async
。
此外,我不能在 class 构造函数中使用 loadEntities
,因为构造函数应标记为 async
,这在 Dart 中是不允许的。
我在 Dart 中使用 async/await
模式错了吗?我如何在 class 构造函数中使用 loadEntities
方法?
不,异步是会传染的,没有办法从异步返回到同步执行。
async
/await
只是 methodThatReturnsFuture().then(...)
用async
标记一个方法只是为了让你在它的内部使用await
。如果没有 async
,您仍然需要 return 一个 Future
以便调用代码仅在 loadAsset()
的结果可用后执行。
您可以直接使用异步调用返回的 Future。这看起来像这样:
class HasAsync {
HasAsync() {
asyncFunction().then((val) {
print(val);
});
}
Future<int> asyncFunction() async {
int val = await otherFunction();
return val;
}
}
您不能在 non-async 函数中使用 await。
因为你用 'flutter' 标记了它,我猜这是在一个 flutter 应用程序中。如果是这种情况,请查看 docs for FutureBuilder - 它可能会对您尝试做的事情有所帮助。
我知道我可能来不及使用这个答案,但我还是写了它,希望有人会觉得它有用。所以这是我的两分钱。
当我第一次尝试弄清楚什么是异步编程以及如何使用它时,我的思考过程与您相同。
既然是关于Flutter的问题,我就用dart来解释一下。
首先,让我们深入了解在异步编程中使用 async await 的基本实际目的。
根据 flutter 文档,async 和 await 关键字的目的是声明性地将函数标记为异步并使用它的结果。
- 要定义异步函数,在函数体之前添加 async
- await 关键字仅在异步函数中有效。
因此,每当您尝试从标记为异步的函数中获取输出时,它别无选择,只能 return Future。请查看以下示例以获得更多说明。
- 首先,你有一个函数可以做一些计算
- 其次,您有一个简单的函数,它通过执行一个简单的 http get 请求从 API 获取数据。
最后是另一个函数,它将处理一些数据并打印一些值。
void processInfo(){ calculateStuff(); Future<Map> decodedData = getDataFromInternet(); getDataProcessed(decodedData); }
所以在同步编程中,这意味着所有三个函数将一个接一个地执行。但是假设第二个函数 getDataFromInternet() 是异步调用的。一个简单的实现如下所示。
Future<Map> getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
return decodedData;
}
因此 return 未来需要上述功能。问题是为什么? 这很简单。在这种情况下,这是因为我们想要 return 某些东西,并且在执行 return 语句时,来自 'get request' 或 的数据可能不可用那一刻。
Thus, the function returns a Future type result which and either be in complete state or incomplete state.
那么我们如何处理这个结果呢?事实上,这可以通过 3 种方式完成。
1.方法一 - 将其作为承诺处理
因此,一旦 getDataFromInternet() 函数 return 在此示例中成为 Future 结果,您就需要像处理 javascript 中的承诺那样处理该未来结果。请参考下面的代码示例。
void getDataProcessed(Future<Map> data) {
print('getting data from future map');
data.then((d) {
print(d);
});
}
2。方法二 - 将父函数标记为异步(传染方式)
void processInfo() async{
calculateStuff();
//now you can simply await that result and process the result rather
//than handling a Future<Map> result in this case.
//Note: it is not required to use future variable because we are awaiting
//for result
Map decodedData = await getDataFromInternet();
getDataProcessed(decodedData);
}
所以在这种情况下,getDataProcessed() 函数看起来像这样。
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
3。方法三 - 在同步函数中使用异步方法的结果(非传染方式)
在这种情况下,processInfo() 函数将略有变化,即 getDataProcessed() 将不再在此方法中调用,看起来像这样。
void processInfo(){
calculateStuff();
getDataFromInternet();
}
我们可以使用 getDataFromInternet() 函数的结果来调用 getDataProcessed(),而不是在 processInfo() 函数中调用 getDataProcessed() function.this 意味着我们不必将 processInfo() 标记为异步,我们可以在执行完 getDataFromInternet() 方法后处理 getDataProcessed() 方法。以下代码示例演示了如何执行此操作。
void getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
//in this case, since we are awaiting for get results response and the
//function is not expected to return anything the data type passed into
//getDataProcessed() function now will be of type Map rather than being type
//Future<Map> . Thus allowing it to be a synchronous function and without
//having to handle the future objects.
getDataProcessed(decodedData);
}
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
So revising back this long answer,
- async/await is just the declarative way to mark asynchronous functions
- when an asynchronous function is called it can be handled in 3 ways.
- get the return Future and handle it like a promise with the use of 'then()' function so no need to mark the parent function async
- mark the parent function async and handle the returned object with await to force the function to wait for the result.
- call the desired function with the output of the async function at the end of the async function. This will allow the main function to continue non-dependent functions while waiting for the results of the async function and one the async function get the results it can go in to the other function at the end and execute it with the data received.
then
和await
是不同的。 await
将在那里停止程序,直到 Future
任务完成。但是 then
不会阻止该程序。 then
中的块将在 Future
任务完成后执行。
如果您希望程序等待 Future
任务,请使用 await
。如果您希望您的程序继续 运行 并且 Future
任务“在后台”执行此操作,请使用 then
.
关于你的问题,建议重新设计。在其他地方执行构造函数所需的加载资产和其他 async
事情。这些任务完成后,再调用构造函数。
最简单的构造函数是 Future()
,它接受一个函数和 return 一个与函数的 return 类型相匹配的未来。稍后函数异步运行,future 以函数的 return 值完成。这是使用 Future()
:
void main() {
final myFuture = Future(() {
print('Creating the future.'); // Prints second.
return 12;
});
print('Done with main().'); // Prints first.
}
要使用完整的值,可以使用then()
。这是每个未来的实例方法,您可以使用它来为未来完成时的值注册回调。你给它一个函数,它接受一个匹配未来类型的参数。一旦 future 以一个值完成,您的函数将被调用为该值。
void main() {
Future.delayed(
const Duration(seconds: 3),
() => 100,
).then((value) {
print('The value is $value.'); // Prints later, after 3 seconds.
});
print('Waiting for a value...'); // Prints first.
}
有关更多信息,请参阅 https://medium.com/dartlang/dart-asynchronous-programming-futures-96937f831137