import 'dart:io';
void main() {
performTask();
}
void performTask() {
task1();
task2();
task3();
}
void task1() {
print('task1');
}
void task2() {
Duration timeDuration = Duration(seconds: 3);
sleep(timeDuration);
print('task2');
}
void task3() {
print('task3');
}
After executing first function that is task1() it throws an error:
Uncaught Error: Unsupported operation: ProcessUtils._sleep
I just hit the same roadblock! Not sure how to get sleep to work, but i found that using async/await is a bit more predictable:
// Unused import
// import 'dart:io'; // Delete me
void main() {
performTask();
}
// No need for async/await here, just the method in which it's used to await Future.delayed
void performTask() {
task1();
task2();
task3();
}
void task1() {
print('task1');
}
// I'm still a bit new to flutter, but as I understand it, best practice is to use Future<T> myFunction() {...} when defining async/await method.
// In this case <T> is <void> because you're not returning anything!
Future<void> task2() async {
Duration timeDuration = Duration(seconds: 3);
// sleep(timeDuration) // Delete Me
await Future.duration(timeDuration); // replacement for the sleep method, comes from the 'package:flutter/material.dart'
print('task2');
}
void task3() {
print('task3');
}
Credit: How can I "sleep" a Dart program
Since Future.duration doesn't
await Future.delayed(const Duration(seconds: 1));
credit: How can I "sleep" a Dart program
Related
I'm currently migrating an App's logic code from C# to Dart and I'm looking for a similiar collection type in Dart to C#s BlockingCollection. I basically want a queue where i can iterate infinitely. If the queue is empty it just waits until a new element is added.
Is that possible in Dart?
Best
You can use a StreamController.
Here I translated the first C# example for BlockingCollection
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class BlockingCollectionDemo
{
static async Task Main()
{
await AddTakeDemo.BC_AddTakeCompleteAdding();
}
}
class AddTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.Take()
// BlockingCollection<T>.CompleteAdding()
public static async Task BC_AddTakeCompleteAdding()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Spin up a Task to populate the BlockingCollection
Task t1 = Task.Run(() =>
{
bc.Add(1);
bc.Add(2);
bc.Add(3);
bc.CompleteAdding();
});
// Spin up a Task to consume the BlockingCollection
Task t2 = Task.Run(() =>
{
try
{
// Consume consume the BlockingCollection
while (true) Console.WriteLine(bc.Take());
}
catch (InvalidOperationException)
{
// An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!");
}
});
await Task.WhenAll(t1, t2);
}
}
}
to dart using a StreamController instead of BlockingCollection, and Future instead of Task.
import 'dart:async';
Future<void> main() async {
await addTakeCompleteAdding();
}
// Demonstrates:
// StreamController<T>.add()
// StreamController<T>.stream
// StreamController<T>.close()
Future<void> addTakeCompleteAdding() async {
StreamController<int> bc = StreamController<int>();
// Spin up a Future to populate the StreamController
Future<void> t1 = Future(() {
bc.add(1);
bc.add(2);
bc.add(3);
bc.close();
});
// Spin up a Future to consume the StreamController
Future<void> t2 = Future(() async {
// Consume consume the StreamController
await for (final element in bc.stream) {
print(element);
}
// Exits the loop when the stream is completed/closed
print("That's All!");
});
await Future.wait([t1, t2]);
}
That said, the StreamController differs a bit from BlockingCollection in that it is not a queue. A Stream in dart by default, can only have one subscription, unless you create a broadcast stream. Stream is more like an async enumerable in C#.
If you really need a queue data structure you can use the async package, which has a StreamQueue class that you can use to wrap the stream from the StreamController.
Here is the above code modified to use a StreamQueue:
import 'dart:async';
import 'package:async/async.dart';
Future<void> main() async {
await addTakeCompleteAdding();
}
// Demonstrates:
// StreamController<T>.add()
// StreamController<T>.stream
// StreamController<T>.close()
// StreamQueue<T>.next
Future<void> addTakeCompleteAdding() async {
StreamController<int> bc = StreamController<int>();
StreamQueue<int> queue = StreamQueue<int>(bc.stream);
// Spin up a Future to populate the StreamController
Future<void> t1 = Future(() {
bc.add(1);
bc.add(2);
bc.add(3);
bc.close();
});
// Spin up a Future to consume the StreamQueue
Future<void> t2 = Future(() async {
try {
while (true) {
// Consume consume the StreamQueue
print(await queue.next);
}
} on StateError catch (e) {
// A StateError means that next was called on a completed collection
print("That's all!");
}
});
await Future.wait([t1, t2]);
}
You can also write your own queue, based on futures instead of a stream:
import "dart:async" show Completer;
import "dart:collection" show Queue;
abstract class BlockingQueue<T> {
factory BlockingQueue() = _BlockingQueue;
Future<T> removeNext();
void add(T value);
}
class _BlockingQueue<T> implements BlockingQueue<T> {
final Queue<T> _writes = Queue();
final Queue<Completer<T>> _reads = Queue();
Future<T> removeNext() {
if (_writes.isNotEmpty) return Future.value(_writes.removeFirst());
var completer = Completer<T>();
_reads.add(completer);
return completer.future;
}
void add(T value) {
if (_reads.isNotEmpty) {
_reads.removeFirst().complete(value);
} else {
_writes.add(value);
}
}
}
You can also consider a double-blocking queue, where the add method also "blocks" if there is no-one to accept the value yet. It's not even that hard,.
import "dart:async" show Completer;
import "dart:collection" show Queue;
abstract class BlockingQueue<T> {
factory BlockingQueue() = _BlockingQueue;
Future<T> removeNext();
Future<void> add(T value);
}
class _BlockingQueue<T> implements BlockingQueue<T> {
final Queue<T> _writes = Queue();
final Queue<Completer<T>> _completers = Queue();
Future<T> removeNext() {
if (_writes.isNotEmpty) {
assert(_completers.isNotEmpty);
var completer = _completers.removeFirst();
completer.complete(_writes.removeFirst());
return completer.future;
}
var completer = Completer<T>();
_completers.add(completer);
return completer.future;
}
Future<void> add(T value) {
if (_writes.isEmpty && _completers.isNotEmpty) {
var completer = _completers.removeFirst();
completer.complete(value);
return completer.future;
}
var completer = Completer<T>();
_completers.add(completer);
_writes.add(value);
return completer.future;
}
}
That said, if you want to use a for (... in ...)-like loop, you probably do want to go with a Stream and use await for (... in theStream).
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
Can someone show me how to implement overriding flutter errors during widget test so I can check for my own custom errors.
I have seen snippets online mentioning this but all of my implementations fail
void main() {
testWidgets('throws an error when scanning unknown term types', (WidgetTester tester) async {
await tester.pumpWidget(injectTestWidget(new ItemScanScreen()));
await tester.enterText(find.byKey(new Key('term')), '');
await tester.tap(find.byIcon(Icons.send));
await tester.pump();
expect(
tester.takeException(),
isInstanceOf<UnrecognizedTermException>(),
reason: 'should have thrown an UnrecognizedTermException error but didn\'t',
);
});
}
the code above fails with the message below even though it looks like it in fact 'caught' my error:
The following UnrecognizedTermException was thrown running a test:
Instance of 'UnrecognizedTermException'
...
I read that you could do something like the snippet below but it did not see how/where to implement it:
final errorHandled = expectAsync0((){});
FlutterError.onError = (errorDetails) {
// handle error
errorHandled();
});
I use the code below in production to log errors to a server.
main.dart:
import 'dart:async';
import 'package:flutter/material.dart';
import 'logging.dart';
void main() async {
FlutterError.onError = (FlutterErrorDetails details) async {
new ErrorLogger().logError(details);
};
runZoned<Future<void>>(() async {
// Your App Here
runApp(MainApp());
}, onError: (error, stackTrace) {
new ErrorLogger().log(error, stackTrace);
});
}
logging.dart:
class ErrorLogger {
void logError(FlutterErrorDetails details) async {
//FlutterError.dumpErrorToConsole(details);
_sendToServer(details.exceptionAsString(), details.stack.toString());
}
void log(Object data, StackTrace stackTrace) async {
// print(data);
// print(stackTrace);
_sendToServer(data.toString(), stackTrace.toString());
}
void _sendToServer(String a, String b) async {
// Implementation here
}
}
This is was design for a test. I switched to wrapping logic in try/catch then running expect() on the "error message text" present concept. ex:
try {
throw new UnrecognizedTermException();
} catch (e) {
setState(() => _status = e.errMsg());
}
// then in my test
expect(find.text('Could not determine the type of item you scanned'), findsOneWidget);
I'd like to run a function over and over, with a delay in between. How can I do this with Dart?
You can use the Timer class to schedule one-shot and repeating functions.
Repeating
Here is how you run a repeating function:
import 'dart:async';
main() {
const oneSec = Duration(seconds:1);
Timer.periodic(oneSec, (Timer t) => print('hi!'));
}
The Timer takes two arguments, a duration and a function to run. The duration must be an instance of Duration. The callback must take a single parameter, the timer itself.
Canceling a repeating timer
Use timer.cancel() to cancel a repeating timer. This is one reason why timer is passed to the callback run from a repeating timer.
One-shot after a delay
To schedule a one-shot function after a delay (execute once, some time in the future):
import 'dart:async';
main() {
const twentyMillis = Duration(milliseconds:20);
Timer(twentyMillis, () => print('hi!'));
}
Notice the callback for a one-shot timer does not take a parameter.
One-shot as soon as possible
You can also request that a function is run as soon as possible, at least one event-loop tick in the future.
import 'dart:async';
main() {
Timer.run(() => print('hi!'));
}
In HTML
Timers even work in HTML. In fact, window.setTimeout was removed, so Timer is the only way to run a function in the future.
5 Sec Timer Example
bool isStopped = false; //global
sec5Timer() {
Timer.periodic(Duration(seconds: 5), (timer) {
if (isStopped) {
timer.cancel();
}
print("Dekhi 5 sec por por kisu hy ni :/");
});
}
Call from any function
sec5Timer();
Stop from any function
isStopped = true;
To dispose you can use this code or technique.
#override
void initState() {
_timer = new Timer.periodic(widget.refreshRate,
(Timer timer) => _updateDisplayTime(inheritedWidget));
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
https://api.dartlang.org/stable/1.24.3/dart-async/Stream/Stream.periodic.html
import 'dart:async';
StreamSubscription periodicSub;
void main() {
periodicSub = new Stream.periodic(const Duration(milliseconds: 500), (v) => v)
.take(10)
.listen((count) => print('tick $count'));
}
or if the counter isn't required just
import 'dart:async';
StreamSubscription periodicSub;
void main() {
periodicSub = new Stream.periodic(const Duration(milliseconds: 500))
.take(10)
.listen((_) => print('tick'));
}
You can also use Future.delayed and await to delay execution:
Future<Null> delay(int milliseconds) {
return new Future.delayed(new Duration(milliseconds: milliseconds));
}
main() async {
await delay(500);
print('Delayed 500 milliseconds');
}
alternative;
import 'dart:async';
Timer interval(Duration duration, func) {
Timer function() {
Timer timer = new Timer(duration, function);
func(timer);
return timer;
}
return new Timer(duration, function);
}
void main() {
int i = 0;
interval(new Duration(seconds: 1), (timer) {
print(i++);
if (i > 5) timer.cancel();
});
}
Opposite to Timer.periodic and Stream.periodic posting my favorite way to handle such a tasks. The advantages:
the first cycle run instantly
the callback can work longer than
interval without any reentrance headache
Completer<bool> periodic(Duration interval, Function(int cycle) callback) {
final done = Completer<bool>();
() async {
var cycle = 0;
while (!done.isCompleted) {
try {
await callback(cycle);
} catch (e, s) {
log("$e", stackTrace: s);
}
cycle++;
await done.future
.timeout(interval)
.onError((error, stackTrace) => null);
}
}();
return done;
}
main() {
final task = periodic(Duration(seconds: 10), (cycle) async {
/// do the periodic tasks here
});
/// main code here
/// and when going to stop the above periodic call
task.complete(true);
}
Functionally identical code to JavaScript (setInterval, setTimeout, clearInterval and clearTimeout):
// ------------------------------
// Import:
import 'dart:async';
// ------------------------------
// Definitions:
void clearTimeout(Timer timer) {
try {
timer.cancel();
} catch (e) {}
}
Timer setTimeout(VoidCallback fn, int millis) {
Timer timer;
if (millis > 0)
timer = new Timer(new Duration(milliseconds: millis), fn);
else
fn();
return timer;
}
void clearInterval(Timer timer) {
try {
timer.cancel();
} catch (e) {}
}
Timer setInterval(VoidCallback fn, int millis) {
Timer timer;
if (millis > 0)
timer = new Timer.periodic(new Duration(milliseconds: millis), (timer) {
fn();
});
else
fn(); // If millis input is too low, only run function once and stop
return timer;
}
// ---------------------------------
// Example:
int myValue = 0;
Timer counter = setInterval((){ myValue++; }, 50);
setTimeout((){
clearInterval(counter);
}, 5000);
I want to create a random sound sequencer, just for learning more about WebAudio and Dart.
My idea is to load some sound examples and play them in random order endlessly.
For that, I've loaded all the files, decoded them in a arraybuffer and put them to play with the following function:
void startAudio()
{
int index=random.nextInt(buffers.length);
print("Audio played [${index}].");
source.buffer=buffers[index];
source.connect(context.destination, 0, 0);
source.start(0);
Timer timer=new Timer(100, this.proceed);
}
void proceed(Timer timer)
{
this.startAudio();
}
The problem is that after some time, the sounds stop playing.
What is going wrong?
Is it the best way to do what I'm trying to do?
If someone wants to test my code, here is the link:
http://cg.usr.sh/Dart/WebAudioTest/WebAudioTest.html
After randomly changing things, I got it working as I was expecting.
import 'dart:html';
import 'dart:math';
import 'dart:async';
class AudioMaker
{
List<String> urls;
AudioContext context;
List<AudioBuffer> buffers;
Random random;
AudioMaker()
{
this.urls=new List<String>();
this.context=new AudioContext();
this.buffers=new List<AudioBuffer>();
this.random=new Random(0);
}
void checkAndStart()
{
if(buffers.length == urls.length)
{
Timer timer=new Timer.repeating(500, this.startAudio);
}
}
void startAudio(Timer timer)
{
int index=random.nextInt(this.buffers.length);
print("Audio played [${index}].");
AudioBufferSourceNode source=context.createBufferSource();
source.buffer=this.buffers[index];
source.connect(context.destination, 0, 0);
source.start(0);
}
void _decodeAudio(url)
{
HttpRequest hr=new HttpRequest.get(url, (req){
this.context.decodeAudioData(req.response, (audio_buff)
{
print("${url} decoded.");
this.buffers.add(audio_buff);
checkAndStart();
}, (evt)
{
print("Error");
});
});
hr.responseType="arraybuffer";
}
void loadAndStart()
{
for(String url in this.urls)
{
this._decodeAudio(url);
}
}
}
main()
{
AudioMaker audioMaker=new AudioMaker();
audioMaker.urls.add("bark.ogg");
audioMaker.urls.add("drip.ogg");
audioMaker.urls.add("glass.ogg");
audioMaker.urls.add("sonar.ogg");
audioMaker.loadAndStart();
}