'Using async value in another function (Dart)
Here's the simplifed code to show what I'm trying to achieve:
class MyClass {
MyClass() {
authorize();
}
String? token;
void authorize() async {
final response =
await Future<String>.delayed(Duration(seconds: 1), (() => "14dw65d1q"));
token = response;
print('Token: $token');
}
String printToken() {
return 'Token: $token';
}
}
void main() {
final myClass = MyClass();
print(myClass.printToken());
}
Basically I'm trying to use token (whose default value of null is changed by authorize()) in another function. The output is:
Token: null
Token: 14dw65d1q
Apparently, main() runs before the value of token gets assigned. Thus, I get a 401 error when I try to use token in the header of a network request. (Not shown in code) How can I make printToken() run after the value of token gets assigned? Making main() async and/or awaiting myClass.printToken() doesn't work. I get 'await' applied to 'String', which is not a 'Future'. when I await 'Token: $token' or myClass.printToken() anyway.
Solution 1:[1]
Your MyClass implementation does not provide any Futures to its consumers, and consumers therefore will not be able to be notified when asynchronous operations in MyClass complete. There are two specific problems:
You've made the
authorizemethod "fire-and-forget" by returningvoid. That instead should return aFuture<void>to allow callers to be notified when it completes.You call
authorizefrom within theMyClassconstructor. However, invoking a constructor must always result in an instance of the class, not aFuture.
Possible fixes for problem #2:
Construct
MyClasswith astaticmethod that returns aFuture.class MyClass { MyClass._(); static Future<MyClass> createInstance() async { var instance = MyClass._(); await instance.authorize(); return instance; } String? token; Future<void> authorize() async { final response = await Future<String>.delayed(Duration(seconds: 1), (() => "14dw65d1q")); token = response; print('Token: $token'); } String printToken() { return 'Token: $token'; } } void main() async { final myClass = await MyClass.createInstance(); print(myClass.printToken()); }Use a
Completer.import 'dart:async'; class MyClass { MyClass() { authorize(); } String? token; final _completer = Completer<void>(); Future<void> get authorizationDone => _completer.future; Future<void> authorize() async { final response = await Future<String>.delayed(Duration(seconds: 1), (() => "14dw65d1q")); token = response; _completer.complete(); print('Token: $token'); } String printToken() { return 'Token: $token'; } } void main() async { final myClass = MyClass(); await myClass.authorizationDone; print(myClass.printToken()); }Move asynchronous operations out of the constructor.
class MyClass { MyClass(); String? token; Future<void> authorize() async { final response = await Future<String>.delayed(Duration(seconds: 1), (() => "14dw65d1q")); token = response; print('Token: $token'); } String printToken() { return 'Token: $token'; } } void main() async { final myClass = MyClass(); await myClass.authorize(); print(myClass.printToken()); }
I recommend using the first approach since it's less error-prone for callers and since you usually shouldn't be using Completer directly.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
