I created a simple Bloc that makes use of BehaviorSubject and I'm trying to test its emitted values, but I keep getting TimeoutException during the test or error in order when I swap the streams added.
The bloc
class ApplicationBloc extends BlocBase{
final _appTitle = BehaviorSubject<String>();
Function(String) get changeTitle => (title) => _appTitle.sink.add(title);
Stream<String> get apptitle => _appTitle.stream;
ApplicationBloc(){
// _appTitle.sink.add('title');
}
#override
void dispose() {
_appTitle.close();
}
}
The Test
test('check title correct', (){
//works
/* appBloc.changeTitle('hi');
expect(appBloc.apptitle, emitsInAnyOrder(['hi']));*/
//doesn't work
appBloc.changeTitle('hi');
appBloc.changeTitle('hello');
expect(appBloc.apptitle, emitsInOrder(['hi', 'hello']));
});
When the title stream emits a single item, it works okay. But when it emits multiple items, it times out.
This is the error I get when the emittion order is swapped
ERROR: Expected: should do the following in order:
* emit an event that 'hi'
* emit an event that 'hello' Actual: '> Which: emitted * hello
which didn't emit an event that 'hi' because it emitted an event that is different.
Expected: hi
Actual: hello
^
Differ at offset 1
NOTE: Everything works as I expect when I change the BehaviorSubject to StreamController
the timeout part may have been a bug, cause today with RxDart 0.24.1, there is no timeout anymore.
but the test doesn't pass still, because BehaviorSubject is only returning the latest value when expect subscribes to .apptitle to listen for values.
for a subject to return everything it was given, use a ReplaySubject.
Related
Short example of what I'm having trouble understanding:
Stream<int> getNumbersWithException() async* {
for (var i = 0; i < 10; i++) {
yield i;
if (i == 3) throw Exception();
}
}
With usage:
getNumbersWithException()
.handleError((x) => print('Exception caught for $x'))
.listen((event) {
print('Observed: $event');
});
This will stop at 3 with the output:
Observed: 0
Observed: 1
Observed: 2
Observed: 3
Exception caught for Exception: foo
From the documentation (https://dart.dev/tutorials/language/streams) and (https://api.dart.dev/stable/2.9.1/dart-async/Stream/handleError.html), this is as expected, as exceptions thrown will automatically close the stream.
Does this mean that the correct way to handle exceptions in a stream, so that subscriptions can be long-lived in such an event, is to handle the exception inside the stream itself? That it is not possible to do so from the outside?
Is this the same for broadcast streams?
If I'm thinking about this in the wrong way, what are some pointers to start thinking right?
I'm currently thinking of streams as being a source of asynchronous data events that occasionally might be error events. From the documentation and examples, it all looks neat, but I'm thinking that wanting to handle errors and otherwise continue observing the data stream is a normal use case. I'm having a hard time writing the code to do so. But, I might be going about this wrong. Any insights will be much appreciated.
Edit: I can add that I've tried various things like using a stream transformer, with the same result:
var transformer = StreamTransformer<int, dynamic>.fromHandlers(
handleData: (data, sink) => sink.add(data),
handleError: (error, stackTrace, sink) =>
print('Exception caught for $error'),
handleDone: (sink) => sink.close(),
);
getNumbersWithException().transform(transformer).listen((data) {
print('Observed: $data');
});
Also, listen() has an optional argument cancelOnError that looks promising, but it defaults to false, so no cigar here.
The generator method
Stream<int> getNumbersWithException() async* {
for (var i = 0; i < 10; i++) {
yield i;
if (i == 3) throw Exception();
}
}
will terminate when you throw an exception.
The throw works normally, it doesn't directly add the exception to the stream. So, it propagates out through the loop and the method body, until the entire method body ends with the thrown exception.
At that point the unhandled exception is added to the stream, and then the stream is closed because the body has ended.
So, the problem is not with the handling, but with the generation of the stream.
You must indeed handle the error locally to avoid it ending the stream generating body.
You can't add more than one error to a stream using throw in an async* method, and the error will be the last thing that stream does.
The availabe hack to actually emit more than one error is to yield the exception:
if (i == 3) yield* () async* { throw Exception(); }();
// or: yield* Stream.fromFuture(Future.error(Exception());
That will emit an exception directly into the generated stream without throwing it locally and ending the generator method body.
I'm trying to load multiple files, in a certain order, inside of Dart. The files are of a modified GTFS (General Transit Feed Specification) type and are such interconnected through 'ids' between the files. Because of this I'm trying to load each file one by one but due to their large size I am using the openRead method of the File class to stream them in line-by-line using the LineSplitter transformer.
How they are being loaded in (start is the byte offset of the openRead method)
Stream<List<int>> getFileStream(String fileName, {int start}) => File(fileName).openRead(start);
// Example Stream
Stream<List<int>> stops = getFileStream("stops.txt", start: 117);
Stream<List<int>> routes = getFileStream("routes.txt", start: 131);
// How the stream data is being read
stops
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(String line) {
List<String> values = line.split(',');
// Do stuff..
},
onDone: () { },
onError: (e) { print(e.toString()); }
);
routes
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(String line) {
List<String> values = line.split(',');
// Do stuff..
// Error occurs here as well since the processing taking place here depends on the processing which takes place when the stops file is read
},
onDone: () { },
onError: (e) { print(e.toString()); }
);
However since I'm loading in multiple files within the same function and they are all streams with a listen callback set and they depend on the processing of the files that came before them to be COMPLETELY finished the program is producing errors since all the files are being read at once line by line and the processing of the other files has not finished.
Ideally, I would like to use an await for (String line in stops) line or something similar, however that produces the following error which I do not know how to solve:
The type 'Stream<List<int>>' used in the 'for' loop must implement Stream with a type argument that can be assigned to 'String'.
This error still shows up even if I do the .transform calls on the stream before the await for line.
I've also tried chaining together the onDone methods of the streams which produced an abomination of code which still didn't work (Lists that were created and added to within the function were empty upon returning???)
It would be nice to use the await for syntax as that produces cleaner code, however I do not want to pollute the whole function tree with async, await functions especially the main() function.
The only thing that worked was using the Completer class from dart:async however the onDone methods needed to be chained for this and the code was barely readable.
Any help as well as some guidance on Futures and async/await would be appreciated.
Have you tried something like this?
await for (var line in stops
.transform(utf8.decoder)
.transform(LineSplitter())) {
List<String> values = line.split(',');
// Do stuff..
// Error occurs here as well since the processing taking place here depends on the processing which takes place when the stops file is read
}
I'm debugging an app, but I need to know some values in the fly, I was wondering if there's a way to print a message in console like console.log using Javascript.
I appreciate the help.
print() is probably what you are looking for. Here's some more info on debugging in flutter.
You can use
print()
function or
debugPrint()
The debugPrint() function can print large outputs.
There are more helpful methods in import 'dart:developer' library and one of them is log().
example:
int i = 5;
log("Index number is: $i");
//output
[log] Index number is: 5
void log(String message, {DateTime time, int sequenceNumber, int level
= 0, String name = '', Zone zone, Object error, StackTrace stackTrace})
Emit a log event.
This function was designed to map closely to the logging information
collected by package:logging.
[message] is the log message
[time] (optional) is the timestamp
[sequenceNumber] (optional) is a monotonically increasing sequence number
[level] (optional) is the severity level (a value between 0 and 2000); see the package:logging Level class for an overview of the
possible values
[name] (optional) is the name of the source of the log message
[zone] (optional) the zone where the log was emitted
[error] (optional) an error object associated with this log event
[stackTrace] (optional) a stack trace associated with this log event
Read more.:
print() is from dart:core and its implementation:
/// Prints a string representation of the object to the console.
void print(Object object) {
String line = "$object";
if (printToZone == null) {
printToConsole(line);
} else {
printToZone(line);
}
}
debugPrint():
/// Prints a message to the console, which you can access using the "flutter"
/// tool's "logs" command ("flutter logs").
///
/// If a wrapWidth is provided, each line of the message is word-wrapped to that
/// width. (Lines may be separated by newline characters, as in '\n'.)
///
/// By default, this function very crudely attempts to throttle the rate at
/// which messages are sent to avoid data loss on Android. This means that
/// interleaving calls to this function (directly or indirectly via, e.g.,
/// [debugDumpRenderTree] or [debugDumpApp]) and to the Dart [print] method can
/// result in out-of-order messages in the logs
// read more here: https://api.flutter.dev/flutter/foundation/debugPrint.html
DebugPrintCallback debugPrint = debugPrintThrottled;
/// Alternative implementation of [debugPrint] that does not throttle.
/// Used by tests.
debugPrintSynchronously(String message, { int wrapWidth })
/// Implementation of [debugPrint] that throttles messages. This avoids dropping
/// messages on platforms that rate-limit their logging (for example, Android).
void debugPrintThrottled(String message, { int wrapWidth })
Read more.
Note that only the print() is taking any type and print to the console. debugPrint() and log() only take String. So, you have to add .toString() or use string interpolation like I shown in provided example snippet.
I tend to do something similar to this
Foo foo;
try{
foo = _someMethod(); //some method that returns a new object
} catch (e) {
print('_someMethod: Foo Error ${foo.id} Error:{e.toString()}'); /*my custom error print message. You don't need brackets if you are printing a string variable.*/
}
Use debug print to avoid logging in production application.
debugPrint("Message");
You can also disable or change debug print implementation in main.dart or any other file like this:
debugPrint = (String message, {int wrapWidth})
{
debugPrintThrottled(message);//Or another other custom code
};
print, debugPrint and others have got some word limit restrictions, if you have something long to print on console, you can:
Create this method:
void printWrapped(String text) {
final pattern = RegExp('.{1,800}'); // 800 is the size of each chunk
pattern.allMatches(text).forEach((match) => print(match.group(0)));
}
Usage:
printWrapped("Your very long string ...");
Source
One more answer for Concatenate with String:
// Declaration
int number = 10;
//Button Action
RaisedButton(
child: Text("Subtract Me"),
onPressed: () {
number = number - 1;
print('You have got $number as result');
print('Before Value is ${number - 1} and After value is ${number + 1}');
},
),
//Output:
flutter: You have got 9 as result
flutter: Before Value is 8 and After value is 10
debugPrint()
Might as well use rather than print() as it attempts to reduce log line drop or being out of order on Android kernel
Refs:
Logging in Flutter
I think this might help you, because, I was also got stuck in many ways of knowing the output of my code in the dart file, hence I got the solution by following the steps, shown in the video.
https://www.youtube.com/watch?v=hhP1tE-IHos
here I have shown an instance of how it works after following the video.
check the left side column where it shows about the value which profile variable carry i.e., null
you can simply use print('whatever you want to print') same as console.log() in javascript.
for more info you can check here.
Note that the print() and log() options both add their own labels at the start of the line, and apply additional formatting that can cause long lines to be truncated. In the case of a dart:io app, you can bypass this interception and mangling entirely by going directly to stdout/stderr, etc. as in stdout.write(), stdout.writeln(), etc. Likewise if you are looking to log explicitly to one or the other. I ran into this issue when adding CLI args to a flutter application.
I use something like this. The print() function can print data up to some limit. So I use this log.
import 'dart:developer';
debugLog({String tag = '', required dynamic value}) {
log("TAG $tag : ${value.toString()}");
}
I have this function which plays a sound inside of Flutter using the audioplayer plugin.
play(int soundFrequency) async {
final result = await audioPlayer.play("urltosound.wav");
}
It works fine. But now I want to be able to play multiple sounds in a row. It seems as if I mess up with the futures. I tried this approach, which is very dirty and ugly but I was just trying to figure things out:
playRandomSequence() async {
final result = audioPlayer.play("urltosound.wav").then(play2(pickRandomSoundFrequency()));
}
play2(int soundFrequency) async {
final result = audioPlayer.play("urltosound.wav");
}
Basically, as soon as the first future is over, I call the next one with the .then() method.
What I get from it is this error:
type '_Future' is not a subtype of type '(dynamic) => dynamic' of 'f' where _Future is from dart:async
How can I fix this?
Thanks
You have the wrong type for the argument of .then(). Try:
.then((_) => play2(pickRandomSoundFrequency()))
You need to pass a function to be called, not call the function when constructing the arguments to then.
If I have the following code:
Future<int> a = new Future(() { print('a'); return 1; });
Future<int> b = new Future.error('Error!');
Future<int> c = new Future(() { print('c'); return 3; });
Future.wait([a, b, c])
.then((List<int> values) => print(values))
.catchError((e) => print(e));
What is printed?
Does the error caught by catchError have any idea of what Future died? Or which Futures remain?
Determining what is printed is pretty easy by running it (do you not have a Dart VM handy?):
a
c
AsyncError: 'Error!'
The documentation for Future.wait() says:
Wait for all the given futures to complete and collect their values.
Returns a future which will complete once all the futures in a list are complete. If any of the futures in the list completes with an error, the resulting future also completes with an error. Otherwise the value of the returned future will be a list of all the values that were produced.
e.error is the value that was created with ('Error!', in this case), so by setting that to something indicating which Future has the error, you can see which Future "died". Of course, if you're not the one creating the error, that doesn't help.
In general, I think you use Future.wait() on a List like this when an error in any of them would be handled similarly. Needing to know which one failed might be a sign that you don't want to wait() on all of them together.
Update: I just realized the question title asks a question not asked in the question body: What happens to the other Futures?
The other Futures keep running. The Future returned by wait() throws whichever error it receives as soon as it receives it, so all return values are lost, and if any of the other Futures throws an error, it isn't caught, but each Future still runs until returning or throwing an error.
AFAIK, you won't know precisely which Future generated the error. But, you can query the error object and get some useful information:
Future.wait([a, b, c])
.then((List<int> values) => print(values))
.catchError((e) {
print(e.error);
print(e.stackTrace);
print(e.runtimeType);
print(e.error.runtimeType);
});
Running this prints:
a
c
Error!
null
AsyncError
String
This tells us that the original error was a String. I don't know how useful that is to you, but its something.