Skip to content

Commit

Permalink
feat(19)!: hooks stream and tailored isolate support added (#39)
Browse files Browse the repository at this point in the history
Now use_isolate has support for streams as well as a way to create
`TailoredStatefulIsolate`s.
  • Loading branch information
lohnn authored Dec 29, 2023
1 parent ca60b97 commit 6f9537f
Show file tree
Hide file tree
Showing 17 changed files with 359 additions and 87 deletions.
14 changes: 8 additions & 6 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: integral_isolates

packages:
- packages/*
- packages/*/example

command:
version:
Expand All @@ -15,17 +16,18 @@ command:
async: ^2.11.0
flutter:
sdk: flutter
flutter_hooks: ^0.20.3
integral_isolates: ^0.4.1
flutter_hooks: ^0.20.4
meta: ^1.9.1
dev_dependencies:
lint: ^2.1.2
test: ^1.24.6
flutter_test:
sdk: flutter
lint: ^2.3.0
test: ^1.25.0
usePubspecOverrides: true

scripts:
analyze:
run: melos exec -c 1 -- flutter analyze --fatal-infos
run: melos exec -- flutter analyze --fatal-infos
description: Run `flutter analyze` for all packages.

doc:
Expand All @@ -37,7 +39,7 @@ scripts:
description: Run `flutter format` for all packages.

test:select:
run: melos exec -c 1 -- dart test
run: melos exec -- dart test
description: Run `flutter test` for selected packages.
select-package:
dir-exists:
Expand Down
11 changes: 5 additions & 6 deletions packages/integral_isolates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ Easy to use isolates for Dart and Flutter.

## Usage

Almost as easy to use as [compute](https://api.flutter.dev/flutter/foundation/compute-constant.html),
but using a long lived isolate. For example:
Almost as easy to use as [compute](https://api.flutter.dev/flutter/foundation/compute.html),
but using a long-lived isolate. For example:

```dart
void main() async {
final statefulIsolate = StatefulIsolate();
final computation = statefulIsolate.isolate;
print(await computation(_isPrime, 7));
print(await computation(_isPrime, 42));
print(await statefulIsolate.compute(_isPrime, 7));
print(await statefulIsolate.compute(_isPrime, 42));
statefulIsolate.dispose();
}
Expand All @@ -29,7 +28,7 @@ bool _isPrime(int value) {

Remember to always dispose once you are done using the isolate to clean up and close the isolate.
```dart
isolated.dispose();
statefulIsolate.dispose();
```

Different backpressure strategies are also supported by just sending in the desired strategy:
Expand Down
5 changes: 2 additions & 3 deletions packages/integral_isolates/lib/src/stateful_isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ sealed class IsolateGetter {
String? debugLabel,
});

// TODO(lohnn): this documentation is incorrect
/// The computation function, a function used the same way as Flutter's
/// compute function, but for a long lived isolate.
/// A computation function that returns a [Stream] of responses from the
/// long lived isolate.
///
/// Very similar to the [compute] function, but instead of returning a
/// [Future], a [Stream] is returned to allow for a response in multiple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ sealed class TailoredIsolateGetter<Q, R> {
String? debugLabel,
});

// TODO(lohnn): this documentation is incorrect
/// The computation function, a function used the same way as Flutter's
/// compute function, but for a long lived isolate.
/// A computation function that returns a [Stream] of responses from the
/// long lived isolate.
///
/// Very similar to the [compute] function, but instead of returning a
/// [Future], a [Stream] is returned to allow for a response in multiple
Expand Down
4 changes: 2 additions & 2 deletions packages/integral_isolates/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ dependencies:
meta: ^1.9.1

dev_dependencies:
lint: ^2.1.2
test: ^1.24.6
lint: ^2.3.0
test: ^1.25.0
26 changes: 16 additions & 10 deletions packages/integral_isolates/test/back_pressure_strategy_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void main() {
final responses = [];

int delayMultiplier = 0;
Future temp(dynamic input) async {
Future runCompute(dynamic input) async {
await Future.delayed(Duration(milliseconds: 50 * delayMultiplier++));
if (input is String) {
try {
Expand All @@ -47,7 +47,7 @@ void main() {
}

await Future.wait([
for (final number in iterable()) temp(number),
for (final number in iterable()) runCompute(number),
]);

isolate.dispose();
Expand All @@ -73,28 +73,34 @@ void main() {
});

test('No backpressure strategy', () async {
final responses = await runIsolate(NoBackPressureStrategy());
expect(responses, [1, 'prefix: test', 2, 3, 4, 5]);
await expectLater(
runIsolate(NoBackPressureStrategy()),
completion([1, 'prefix: test', 2, 3, 4, 5]),
);
});

test('Discard new backpressure strategy', () async {
final responses = await runIsolate(DiscardNewBackPressureStrategy());
expect(responses, [1, 'prefix: test']);
await expectLater(
runIsolate(DiscardNewBackPressureStrategy()),
completion([1, 'prefix: test']),
);
});

test('Replace backpressure strategy', () async {
final responses = await runIsolate(ReplaceBackpressureStrategy());
expect(responses, [1, 5]);
await expectLater(
runIsolate(ReplaceBackpressureStrategy()),
completion([1, 5]),
);
});

test('Combine backpressure strategy', () async {
final responses = await runIsolate(
final future = runIsolate(
CombineBackPressureStrategy((oldData, newData) {
if (oldData is num && newData is num) return oldData + newData;
return newData;
}),
);
expect(responses, [1, 14]);
expect(future, completion([1, 14]));
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,24 @@ void main() {
});

test('No backpressure strategy', () async {
final responses = await runIsolate(NoBackPressureStrategy());
expect(responses, [1, 2, 3, 4, 5]);
expect(
runIsolate(NoBackPressureStrategy()),
completion([1, 2, 3, 4, 5]),
);
});

test('Discard new backpressure strategy', () async {
final responses = await runIsolate(DiscardNewBackPressureStrategy());
expect(responses, [1, 2]);
expect(
runIsolate(DiscardNewBackPressureStrategy()),
completion([1, 2]),
);
});

test('Replace backpressure strategy', () async {
final responses = await runIsolate(ReplaceBackpressureStrategy());
expect(responses, [1, 5]);
expect(
runIsolate(ReplaceBackpressureStrategy()),
completion([1, 5]),
);
});

test('Combine backpressure strategy', () async {
Expand All @@ -80,12 +86,15 @@ void main() {
);
isolate.compute((message) => message + 2, 2);

final responses = await runIsolate(
final future = runIsolate(
CombineBackPressureStrategy<int, int>((oldData, newData) {
return oldData + newData;
}),
);
expect(responses, [1, 14]);
expect(
future,
completion([1, 14]),
);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ void main() {

test('Send different data types and expect answers', () async {
expect(
await isolate.compute((number) => number + 2, 1),
equals(3),
isolate.compute((number) => number + 2, 1),
completion(3),
);

expect(
await isolate.compute((text) => 'prefix: $text', 'testing'),
equals('prefix: testing'),
isolate.compute((text) => 'prefix: $text', 'testing'),
completion('prefix: testing'),
);
});

Expand Down
27 changes: 23 additions & 4 deletions packages/use_isolate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ The power of [integral_isolates](https://pub.dev/packages/integral_isolates) nea

## Usage

Using an isolate in a hook has never been simpler. With the use of `useIsolate()` we you can get a
compute function similar to [compute](https://api.flutter.dev/flutter/foundation/compute-constant.html)
Using an isolate in a hook has never been simpler. With the use of `useIsolate()` you can get a
compute function similar to [compute](https://api.flutter.dev/flutter/foundation/compute.html)
but that lives longer. You don't have to care about lifecycle, the hook handles that for you.

Example:

```dart
class TestingIsolateHook extends HookWidget {
const TestingIsolateHook({super.key});
Expand All @@ -19,7 +20,7 @@ class TestingIsolateHook extends HookWidget {
return TextButton(
onPressed: () async {
var isPrime = await isolate(_isPrime, number.value);
final isPrime = await isolate.compute(_isPrime, number.value);
print('${number.value} is a prime number? ${isPrime}');
number.value += 1;
},
Expand All @@ -45,10 +46,28 @@ class TestingIsolateHook extends HookWidget {

Just as integral_isolates, this hook supports backpressure strategies, just send a strategy in as
parameter:

```dart
final isolate = useIsolate(backpressureStrategy: DiscardNewBackPressureStrategy());
```

### What about TailoredStatefulIsolate?

You might know that you can create tailored stateful isolates with
[integral_isolates](https://pub.dev/packages/integral_isolates). This is now also possible with use_isolate.

To create a tailored isolate that takes a `double` as the input parameter and returns an `int`, just use this hook
instead:

```dart
useIsolate(backpressureStrategy: DiscardNewBackPressureStrategy());
final isolate = useTailoredIsolate<double, int>();
```

### Breaking change

* `use_isolate` v0.3.0: now returns the whole isolate, requiring you to explicitly type out `isolate.compute(..)`
instead of just `isolate(..)`. This is due to support for `isolate.computeStream(..)` is added.

## Additional information

You could expect this API to be _mostly_ stable, but implementation of the underlying package
Expand Down
34 changes: 18 additions & 16 deletions packages/use_isolate/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class IsolateTestingWidget extends HookWidget {

Future<String?> safeHello(_MethodInput input) async {
try {
return await tailoredIsolate(_hello, input);
return await tailoredIsolate.compute(_hello, input);
} catch (e) {
return null;
}
Expand All @@ -83,47 +83,49 @@ class IsolateTestingWidget extends HookWidget {
),
TextButton(
onPressed: () async {
final hi = await isolate(
final hi = await isolate.compute(
_hello, _MethodInput("hi", delay: const Duration(seconds: 2)));
print(hi);
},
child: const Text('Run one'),
),
TextButton(
onPressed: () async {
final yo = await isolate(_hello, _MethodInput("yo"));
final yo = await isolate.compute(_hello, _MethodInput("yo"));
print(yo);
},
child: const Text('Run other'),
),
TextButton(
onPressed: () async {
final hi = await isolate(_hello, _MethodInput("hi"));
final hi = await isolate.compute(_hello, _MethodInput("hi"));
print(hi);
final yo = await isolate(_hello, _MethodInput("yo"));
final yo = await isolate.compute(_hello, _MethodInput("yo"));
print(yo);
},
child: const Text('Run two in queue'),
),
TextButton(
onPressed: () async {
isolate(_hello, _MethodInput("hi")).then(print);
isolate(
_hello,
_MethodInput(
"yo",
delay: const Duration(milliseconds: 500),
),
).then(print);
isolate.compute(_hello, _MethodInput("hi")).then(print);
isolate
.compute(
_hello,
_MethodInput(
"yo",
delay: const Duration(milliseconds: 500),
),
)
.then(print);
},
child: const Text('Run two in parallel, listen to value'),
),
TextButton(
onPressed: () async {
final responses = await Future.wait(
[
isolate(_hello, _MethodInput("hi")),
isolate(
isolate.compute(_hello, _MethodInput("hi")),
isolate.compute(
_hello,
_MethodInput("ho", delay: const Duration(milliseconds: 500)),
),
Expand Down Expand Up @@ -151,7 +153,7 @@ class IsolateTestingWidget extends HookWidget {
),
TextButton(
onPressed: () async {
final hi = await isolate(_error, _MethodInput("hi"));
final hi = await isolate.compute(_error, _MethodInput("hi"));
print(hi);
},
child: const Text('Error'),
Expand Down
12 changes: 6 additions & 6 deletions packages/use_isolate/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ publish_to: 'none'
version: 0.1.0

environment:
sdk: ">=2.18.0 <3.0.0"
sdk: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"

dependencies:
flutter:
sdk: flutter
flutter_hooks:
integral_isolates: ^0.4.0
use_isolate: ^0.2.0
cupertino_icons: ^1.0.2
flutter_hooks: ^0.20.4
integral_isolates: ^0.4.1
use_isolate: ^0.2.0+2

dev_dependencies:
flutter_lints: ^2.0.0
lint: ^2.3.0

flutter:
uses-material-design: true
Loading

0 comments on commit 6f9537f

Please sign in to comment.