I am testing some middleware code. When the function middleware.call is provoked I get different results depending on whether I use the keyword await when calling it.
This is testing code:
final actionLog = <dynamic>[];
final Function(dynamic) next = (dynamic action){
actionLog.add(action);
};
test('Fetching a dictionary entry successfully', () async {
DictionaryEntry dictionaryEntry = new DictionaryEntry(2333, 'after', null, null);
when(
mockApi
.getDictionaryEntry('after', any, any)
).thenAnswer((_) => Future.value(dictionaryEntry));
final action = FetchDictionaryEntryAction('after');
await dictionaryMiddleware.call(mocksStore, action, next);
// length of action log is 2
// when I don't use await before calling dictionaryMiddleware.call
// but its 4 when using await before calling the function.
print("length of action log " + actionLog.length.toString());
print(actionLog);
});
This is the middleware:
#override
Future<void> call(
Store<AppState> store, dynamic action, NextDispatcher next) async {
next(action);
if(action is FetchDictionaryEntryAction){
await _fetchDictionaryEntry(action.speechText,
action.fromLanguage, action.toLanguage, next
);
}
}
Future<void> _fetchDictionaryEntry(
String speechText,
String fromLanguage,
String toLanguage,
NextDispatcher next
) async {
if(speechText == null || speechText?.length == 0){
next(ReceivedDictionaryEntryAction(dictionaryEntry: null));
return;
}
next(RequestingDictionaryEntryAction());
try{
final DictionaryEntry dictionaryEntry =
await this.api.getDictionaryEntry(speechText, fromLanguage, toLanguage);
next(ReceivedDictionaryEntryAction(dictionaryEntry: dictionaryEntry));
next(CacheDictionaryEntryAction(dictionaryEntry));
}catch(e){
next(ErrorLoadingDictionaryEntryAction);
}
}
I am wondering why does next() (the dispatcher in the middleware) get called only 2 times when not using await as apposed to getting called 4 times when using await which is the normal behavior.
Related
I wrote the following code and encountered the error The provider AutoDisposeFutureProvider<Data>#d1e31(465-0041) was disposed before a value was emitted.
I thought it was strange, so I debugged FutureProvider's onDispose and found that it was disposed during the await of the API call, which is confirmed by the output of disposed!
class HogeNotifier extends StateNotifier<Hoge> {
onFormSubmitted(String input) async {
final value = await _reader(searchProvider(input).future); // The provider AutoDisposeFutureProvider<Data>#d1e31(465-0041) was disposed before a value was emitted.
// execute by using value
}
}
final searchProvider =
FutureProvider.autoDispose.family<Data, String>((ref, value) async {
ref.onDispose(() {
print("disposed!");
});
try {
final result = await dataSource.find(keyword: value); //call api asynchronously
return Future.value(result);
} on Exception catch (e) {
return Future<Data>.error(e);
}
});
How can I solve this problem?
I am having trouble understanding why one piece of code prints the future is null
void main() async {
task1();
String str = await task2();
task3(str);
}
void task1() {
print('ring');
}
Future<String> task2() async {
Duration dur = Duration(seconds: 3);
String res;
await Future.delayed(dur, () {
res = 'a bright one!';
return res; // return statement inside the callback.
});
}
void task3(String str) {
print('the future is $str');
}
while this works properly and has the expected behavior of printing 'the future is a bright one'
void main() async {
task1();
String str = await task2();
task3(str);
}
void task1() {
print('ring');
}
Future<String> task2() async {
Duration dur = Duration(seconds: 3);
String res;
await Future.delayed(dur, () {
res = 'a bright one!';
});
return res;
}
void task3(String str) {
print('the future is $str');
}
I am new to asynchronous programming but my understanding is that the callback that is the second argument in Future.delayed is executed after a delay what I don't understand why the placement of the return statement here breaks the code. I tried to run the code in debug mode to trace the code but I didn't understand what is exactly happening. All help is greatly appreciated
I am still a beginner on dart flutter, now I am trying to retrieve data from the REST API and socket.IO. at this time I have a confusing problem, I have tried searching on the internet for 3 days, but there is no solution. I have async and await scripts, but the function I added await doesn't give any response and still pause.
it is assumed that I have two different files, the first is the main file and the second is the helper file.
main.dart
Future<List<ChatTile>> fetchChat(socketutil,id) async {
socketutil.join(id); //STACK IN HERE
SharedPreferences prefs = await SharedPreferences.getInstance();
String messagePrefs = prefs.getString('messagePrefs');
print("DUA");
return await compute(parseListChat, messagePrefs);
}
helper.dart
Future<void> join(String id_room) async {
String jsonData ='{"room_id" : "$id_room","user_id" : "5a91687811138e74009839c9","user_name" : "Denis Muhammad Ramdan","user_photo" : "photo.jpg","user_status" : "1"}';
socketIO.sendMessage("join", jsonData, null);
//subscribe event
return await socketIO.subscribe("updateMessageList", (result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
print('SATU');
return await result;
});
}
my question is there something wrong with my code, and how is the best way?
many thanks,
I suggest you to add await_only_futures to your analyzer config
analysis_options.yaml
lint:
rules:
- await_only_futures
You also don't need to do return await something since your function already return a future, this is redondant.
And from what I see of the socketio subscribe method, it does not return the result like you expect but use a callback and does not return it (https://pub.dartlang.org/documentation/flutter_socket_io/latest/flutter_socket_io/SocketIO/subscribe.html)
to handle this you should use a Completer
final completer = Completer<String>()
socketIO.subscribe("updateMessageList", (result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
socketIO.unSubscribe("updateMessageList");
completer.complete(result);
});
return completer.future;
you probably want to handle error when there is using completer.completeError(error)
Update
You can alos convert the subscription to a Dart Stream to handle more case.
StreamController<String> controller;
Stream<String> get onUpdateMessageList {
if (controller != null) return controller.stream;
constroller = StreamController<String>.broadcast(
onCancel: () => socketIO.unSubscribe("updateMessageList"),
);
socketIO.subscribe("updateMessageList", constroller.add);
return controller.stream;
}
Future<StreamSubscription> join(String id_room) async {
...
return onUpdateMessageList.listen((result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
});
}
I want to return a String from an async function but I get a Future
What am I doing wrong;
Example
main() {
String s;
s = dummy("http://www.google.com");
}
String dummy(String s) {
String response;
response = readURL(s);
return response;
}
Future<String> readURL(String requestString) async {
String response = await http.read(requestString);
print(response);
return response;
}
Error:
type '_Future' is not a subtype of type 'String' of 'response'.
A function that's annotated with async will always return a Future.
so when you call readUrl(s) you can await its result.
To use await, the caller (here your main function) has to be marked as async. So the end result could look like this:
main() async {
String s = await dummy("http://www.google.com");
}
Future<String> dummy(String s) async {
String response = await readURL(s);
return (response);
}
Future<String> readURL(String requestString) async {
String response = await http.read(requestString);
print(response);
return(response);
}
The thing to notice here: If you use await in a function, it is now considered as function that returns a Future. So every function you convert to be async will now return a Future.
Here is the Simple Two way to get value from Function with return type Future<Type>
1- First way (best way, as you call this code from any file)
FutureFunctionName.then((val) {
val contains data
});
For example- (I am posting one from real example)
Future<String> getUserAgents() async {
String userAgent;
await FlutterUserAgent.init();
userAgent = FlutterUserAgent.webViewUserAgent;
return userAgent;
}
String userAgent;
getUserAgents().then((val) {
userAgent = val;
});
print(userAgent); // you will get output
2- Second way (use a global variable to get data)
String userAgent;
Future<void> getUserAgents() async {
await FlutterUserAgent.init();
userAgent = FlutterUserAgent.webViewUserAgent;
}
print(userAgent);
I have next controller
public async Task<ActionResult> ImageAsync(int id)
{
var img = await _repository.GetImageAsync(id);
if (img != null)
{
return File(img, "image/jpg"); //View(img);
}
byte[] res = new byte[0];
return File(res, "image/jpg");
}
and method in repository
public async Task<byte[]> GetImage(int imageId)
{
try
{
var dbCtx = new smartbags_storeEntities();
var res = await dbCtx.GoodImages.SingleAsync(d => d.ImageId == imageId);
return res != null ? res.ImageData : null;
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<byte[]> GetImageAsync(int imageId)
{
byte[] img = await Task.Run(() =>
{
var res = GetImage(imageId).Result;
if (res != null)
{
var wi = new System.Web.Helpers.WebImage(res);
wi.AddTextWatermark("info");
return wi.GetBytes();
}
return null;
});
return img;
}
but execution of image reading is freezing on line
var res = await dbCtx.GoodImages.SingleAsync(d => d.ImageId == imageId);
What I am doing in wrong way when try to read data from data base in async style ?
The call to the property Result of a Task is a blocking call and the continuation of the await won't be able to be posted to run.
Once you already have a Task returning method, why didn't you just use await?
public async Task<byte[]> GetImageAsync(int imageId)
{
var res = await GetImage(imageId);
if (res != null)
{
var wi = new System.Web.Helpers.WebImage(res);
wi.AddTextWatermark("info");
return wi.GetBytes();
}
return null;
}
The funny thing about that line is that it calls SingleAsync, which is a TAP extension method for observables.
I have never used a data repository that exposed its collections as observables, though I suppose it is possible. My first guess is that [the task returned by] SingleAsync isn't completing because the GoodImages observable isn't completing. Note that SingleAsync must continue scanning after it sees a match to ensure that it is the only match; FirstAsync is more forgiving and will complete as soon as it sees the first match.
On a side note, I do recommend using await instead of Result and not using Task.Run on the server. So Paulo's answer is good in that regard, though in this case Result is not causing a deadlock.