I have very small app:
main() async
{
Stream stream = Stream.fromIterable([1,2,3,4]);
stream.listen(
(d) { print(d); },
onDone: () { print("all done"); }
);
}
I can't understand why I can't call onData in the same manner as onDone like:
onData: (d) { print(d); }
Docs
The onData parameter to listen is a positional parameter, and the onDone parameter is a named parameter. All parameters are one or the other, and it determines how you pass an argument for that parameter.
To pass an argument for a named parameter, you must use the name, so you write onDone: () { ... }.
To pass an argument for a positional parameter, you just write the value. You cannot use the name.
So, to call listen with both, you write:
stream.listen(
(data) { handleData(...); },
onDone: () { handleDone(...); }
);
which passes one positional argument and one named arugment with the name onDone.
Related
I want to create a reusable function in Electron.js to handle Saving data irrespective of the model(e.g User, Employee, Product),so I passed Model as an argument, then call the specific Model during when the function is called.
but I get this error
Error: Expected handler to be a function, but found type 'object'
This is my code
const User = require( '../database/models/Users.js');
ipcMain.handle('user:create', saveData(User));
async function saveData(_, data,Model) {
try {
const user = await Model.insert(data);
return user;
} catch (e) {
console.log(e.message);
}
}
ipcMain.handle('user:create', saveData(User)); call function saveData(User) after app is started and it returns object. if you want to assign function to 'user:create' then without parameters it's ipcMain.handle('user:create', saveData); but with parameters it's.
ipcMain.handle('user:create', () => saveData(User));
is the same as
ipcMain.handle('user:create', function () {
return saveData(User)
});
I have a Stimulus controller inside which I have a setSegments function and then this code in the connect() method:
connect() {
const options = {
overview: {
container: document.getElementById('overview-container'),
waveformColor: 'blue',
},
mediaElement: document.querySelector('audio'),
dataUri: {
arraybuffer: document.getElementById('normal-audio-button').dataset.waveform
},
emitCueEvents: true,
};
Peaks.init(options, function (err, peaks) {
window.instance = peaks;
window.speed = "normal";
setSegments()
instance.on('segments.enter', function (segment) {
const segmentCard = document.getElementById(segment.id)
segmentCard.focus({preventScroll: true})
window.currentSegment = segment
});
});
}
setSegments() {
alert("segment set up)
}
I'm tryng to call setSegments() inside the Peaks.init function but it doesn't work because of the function's scope. I'm just not sure how to get around this. I tried calling this.setSegments() instead but it doesn't help.
What's the correct way of accessing the function in this case?
Thanks
The problem is that this is a bit confusing when working with JavaScript, however a way to think about it that it is the current context.
For example, when your code is running in the browser console or not in another function this is the global window object. When you are directly in the controller's connect method this is the controller's instance.
However, when you pass a function to Peaks.init that function creates it's own new context where this is the function's context and no longer the controller instance.
There are three common workarounds to calling this.setSegments;
1. Set a variable that is outside the function scope
As per your solution, const setSegments = this.setSegments; works because you are creating a reference outside the function scope and functions have access to this.
connect() {
const options = {}: // ... Options
// this is the controller's instance
const setSegments = this setSegments;
Peaks.init(options, function (err, peaks) {
// this is the peaks init handler context
window.instance = peaks;
// this.setSegments(): - will not work
setSegments();
instance.on('segments.enter', function (segment) {
// this is the event (usually)
});
});
}
2. Use bind to override the function'sthis
You can pull your function out to a variable and then add .bind(this) to it so that when the function is called it will use the this from the controller instance instead.
connect() {
const options = {}: // ... Options
// this is the controller's instance
const myFunction = function (err, peaks) {
// this is the BOUND this provided by the bind command and will be the controller's instance
window.instance = peaks;
this.setSegments():
instance.on('segments.enter', function (segment) {
// this is the event (usually)
});
};
myFunction.bind(this);
Peaks.init(options, myFunction);
}
3. Use an arrow function (easiest)
You should be able to use an arrow function in modern browsers or if you have a build tool running it may transpiled this for older browsers.
Instead of function() {} you use () => {} and it grabs the this from the parent function context instead.
connect() {
const options = {}: // ... Options
// this is the controller's instance
Peaks.init(options, (err, peaks) => {
// this is now the controller's instance and NOT the peak handler context
window.instance = peaks;
this.setSegments():
instance.on('segments.enter', function (segment) {
// this is the event (usually) and is NOT the controller instance as arrow function not used here.
});
});
}
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this for more details
I don't know if it's the best way to do it but adding the following right after the beginning of the connect method did the trick:
let setSegments = this.setSegments
I use Mockito for writing tests on Flutter. I have a mocked class and method with arguments that are functions - Function() and this method returns StreamSubscription. I need to pass these arguments to the call of listen() function but can't find a way to do it. (See example)
Could somebody help me, please?
I tried to pass them with argThat(anyNamed('nameOfArgument') like in the example, but tests trows error - The "anyNamed" argument matcher is used outside of method stubbing (via when)
class MockPhotoLibraryService extends Mock implements PhotoLibraryService {}
PhotoLibraryService setupMockPhotoLibraryService() {
final photoLibraryService = MockPhotoLibraryService();
when(
photoLibraryService.getPhotosForPeriod(
onData: anyNamed('onData'),
onDone: anyNamed('onDone'),
onError: anyNamed('onError')),
).thenAnswer((_) => Stream<Photo>.fromFuture(
Future<Photo>.delayed(Duration(milliseconds: 50), () => Photo()))
.listen(argThat(anyNamed('onData')), //need to pass argument named onData
onDone: argThat(anyNamed('onDone')), //need to pass argument named onDone
onError: argThat(anyNamed('onError')), //need to pass argument named onError
cancelOnError: true));
return photoLibraryService;
}
I need these arguments functions to be called by the Future for the correct work of my testable widget.
You can get access to the original call parameters through Invocation object. It is passed as a parameter to the thenAnswer callback function.
when(photoLibraryService.getPhotosForPeriod(
onData: anyNamed('onData'),
onDone: anyNamed('onDone'),
onError: anyNamed('onError'),
)).thenAnswer((Invocation invocation) {
final namedArgs = invocation.namedArguments;
final onData = namedArgs[Symbol('onData')] as Function(Photo);
final onDone = namedArgs[Symbol('onDone')] as Function();
final onError = namedArgs[Symbol('onError')] as Function(dynamic);
return Stream<Photo>.fromFuture(
Future<Photo>.delayed(Duration(milliseconds: 50), () => Photo()),
).listen(onData, onDone: onDone, onError: onError, cancelOnError: true);
});
I am using Dart2
What I want to do is to force developer to use callback that accepts no or at most 1 argument.
For example, having following method:
void doYourJob(void onComplete([result])){ //this is what I have tried, buts its wrong - requires ([arg])=> callback
.... do your job
onComplete(result);
}
I would like to be able to use that method in two ways eg:
doYourJob(()=>doStuff);
and
doYourJob((result)=>doMoreStuffWithResult(result));
Is it possible to do something like this?
No. What you are trying to do is not possible.
You want to have a function type which accepts functions taking either zero or one argument. That is, you want to be able to pass a unary function and a nullary function.
That is, a function type which is a supertype of both void Function() and void Function(Object).
That is not the same as a function type with an optional parameter. Such a function type requires that all arguments must be callable both with zero and one argument. You cannot pass a pure unary function to that, because that function cannot be called with zero arguments.
There is no function type which is a supertype of both void Function() and void Function(Object). The nearest supertype is Function, which accepts any function, not just unary and nullary ones. You can use that, but you lose the type checking.
You can do this by creating a typedef
typedef Callback = Null Function([String data]);
void doYourJob(Callback onComplete) {
onComplete('Data');
onComplete();
}
OR
You can pass the Function directly
void doYourJob(Null Function([String data]) onComplete) {
onComplete('Data');
onComplete();
}
You can call this method like this
void main() {
doYourJob(([String data]) {
print('DATA: $data');
});
}
I've implemented the stream transformer. Please note that it is only an exercise (in order to learn Dart). This transformer converts integers into strings. I give the code below, and you can also find it on GitHub.
// Conceptually, a transformer is simply a function from Stream to Stream that
// is encapsulated into a class.
//
// A transformer is made of:
// - A stream controller. The controller provides the "output" stream that will
// receive the transformed values.
// - A "bind()" method. This method is called by the "input" stream "transform"
// method (inputStream.transform(<the stream transformer>).
import 'dart:async';
/// This class defines the implementation of a class that emulates a function
/// that converts a data with a given type (S) into a data with another type (T).
abstract class TypeCaster<S, T> {
T call(S value);
}
/// This class emulates a converter from integers to strings.
class Caster extends TypeCaster<int, String> {
String call(int value) {
return "<${value.toString()}>";
}
}
// StreamTransformer<S, T> is an abstract class. The functions listed below must
// be implemented:
// - Stream<T> bind(Stream<S> stream)
// - StreamTransformer<RS, RT> cast<RS, RT>()
class CasterTransformer<S, T> implements StreamTransformer<S, T> {
StreamController<T> _controller;
bool _cancelOnError;
TypeCaster<S, T> _caster;
// Original (or input) stream.
Stream<S> _stream;
// The stream subscription returned by the call to the function "listen", of
// the original (input) stream (_stream.listen(...)).
StreamSubscription<S> _subscription;
/// Constructor that creates a unicast stream.
/// [caster] An instance of "type caster".
CasterTransformer(TypeCaster<S, T> caster, {
bool sync: false,
bool cancelOnError: true
}) {
_controller = new StreamController<T>(
onListen: _onListen,
onCancel: _onCancel,
onPause: () => _subscription.pause(),
onResume: () => _subscription.resume(),
sync: sync
);
_cancelOnError = cancelOnError;
_caster = caster;
}
/// Constructor that creates a broadcast stream.
/// [caster] An instance of "type caster".
CasterTransformer.broadcast(TypeCaster<S, T> caster, {
bool sync: false,
bool cancelOnError: true
}) {
_cancelOnError = cancelOnError;
_controller = new StreamController<T>.broadcast(
onListen: _onListen,
onCancel: _onCancel,
sync: sync
);
_caster = caster;
}
/// Handler executed whenever a listener subscribes to the controller's stream.
/// Note: when the transformer is applied to the original stream, through call
/// to the method "transform", the method "bind()" is called behind the
/// scenes. The method "bind()" returns the controller stream.
/// When a listener is applied to the controller stream, then this function
/// (that is "_onListen()") will be executed. This function will set the
/// handler ("_onData") that will be executed each time a value appears
/// in the original stream. This handler takes the incoming value, casts
/// it, and inject it to the (controller) output stream.
/// Note: this method is called only once. On the other hand, the method "_onData"
/// is called as many times as there are values to transform.
void _onListen() {
_subscription = _stream.listen(
_onData,
onError: _controller.addError,
onDone: _controller.close,
cancelOnError: _cancelOnError
);
}
/// Handler executed whenever the subscription to the controller's stream is cancelled.
void _onCancel() {
_subscription.cancel();
_subscription = null;
}
/// Handler executed whenever data comes from the original (input) stream.
/// Please note that the transformation takes place here.
/// Note: this method is called as many times as there are values to transform.
void _onData(S data) {
_controller.add(_caster(data));
}
/// This method is called once, when the stream transformer is assigned to the
/// original (input) stream. It returns the stream provided by the controller.
/// Note: here, you can see that the process transforms a value of type
/// S into a value of type T. Thus, it is necessary to provide a function
/// that performs the conversion from type S to type T.
/// Note: the returned stream may accept only one, or more than one, listener.
/// This depends on the method called to instantiate the transformer.
/// * CasterTransformer() => only one listener.
/// * CasterTransformer.broadcast() => one or more listener.
Stream<T> bind(Stream<S> stream) {
_stream = stream;
return _controller.stream;
}
// TODO: what should this method do ? Find the answer.
StreamTransformer<RS, RT> cast<RS, RT>() {
return StreamTransformer<RS, RT>((Stream<RS> stream, bool b) {
// What should we do here ?
});
}
}
main() {
// ---------------------------------------------------------------------------
// TEST: unicast controller.
// ---------------------------------------------------------------------------
// Create a controller that will be used to inject integers into the "input"
// stream.
StreamController<int> controller_unicast = new StreamController<int>();
// Get the stream "to control".
Stream<int> integer_stream_unicast = controller_unicast.stream;
// Apply a transformer on the "input" stream.
// The method "transform" calls the method "bind", which returns the stream that
// receives the transformed values.
Stream<String> string_stream_unicast = integer_stream_unicast.transform(CasterTransformer<int, String>(new Caster()));
string_stream_unicast.listen((data) {
print('String => $data');
});
// Inject integers into the "input" stream.
controller_unicast.add(1);
controller_unicast.add(2);
controller_unicast.add(3);
// ---------------------------------------------------------------------------
// TEST: broadcast controller.
// ---------------------------------------------------------------------------
StreamController<int> controller_broadcast = new StreamController<int>.broadcast();
Stream<int> integer_stream_broadcast = controller_broadcast.stream;
Stream<String> string_stream_broadcast = integer_stream_broadcast.transform(CasterTransformer<int, String>.broadcast(new Caster()));
string_stream_broadcast.listen((data) {
print('Listener 1: String => $data');
});
string_stream_broadcast.listen((data) {
print('Listener 2: String => $data');
});
controller_broadcast.add(1);
controller_broadcast.add(2);
controller_broadcast.add(3);
}
The class CasterTransformer<S, T> extends the abstract class StreamTransformer<S, T>.
Thus, it implements the method StreamTransformer<RS, RT> cast<RS, RT>().
On the documentation, it is said that :
The resulting transformer will check at run-time that all data events of the stream it transforms are actually instances of S, and it will check that all data events produced by this transformer are actually instances of RT.
See: https://api.dartlang.org/stable/2.1.0/dart-async/StreamTransformer/cast.html
First, I think that there is a typo in this documentation : it should say "...it transforms are actually instances of RS" (instead of S).
However, this seems obscure to me.
Why do we need a stream transformer to check values types ? The purpose of a transformer is to transform, isn't it ? If the purpose of a component is to check, so why don't we call it a checker ?
And, also, why would we need to check that the transformer (we implement) produces the required data ? If it doesn't, then we face a bug that should be fixed.
Can someone explain the purpose of the method Cast() ?
The cast method is there to help typing the operation.
If you have a StreamTransformer<num, int>, it transforms numbers to integers (say, by calling .toInt() on them and then adding 42, because that is obviously useful!).
If you want to use that transformer in some place that expects a StreamTransformer<int, num>, then you can't. Since num is not a sub-type of int, the transformer is not assignable to that type.
But you know, because you understand how a stream transformer actually works, that the first type argument is only used for inputs. Something that accepts any num should safely be useable where it's only given ints.
So, to convince the type system that you know what you are doing, you write:
StreamTransformer<int, num> transform = myTranformer.cast<int, num>();
Now, the tranformer takes any integer (RS), checks that it's a num (S), passes it to myTransformer which calls toInt() and adds 42, then the resulting int (T) is passed back and transformer checks that it is a num (RT) and emits that.
Everything works and the type system is happy.
You can use cast to do things that will never work at run-time, because all it does is to add extra run-time checks that convinces the static type system that things will either succeed or throw at those checks.
The easiest way to get an implementation of StreamTransformer.cast is to use th e StreamTransformer.castFrom static method:
StreamTransformer<RS, RT> cast<RS, RT>() => StreamTransformer.castFrom(this);
That will use the system's default cast wrapper on your own transformer.