Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Fable.Core/Fable.Core.Dart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ type Future<'T> = interface end
[<ImportMember "dart:async">]
type Stream<'T> = interface end

type Async with
static member AwaitFuture(future: Future<'T>) : Async<'T> = nativeOnly

static member StartAsFuture(workflow: Async<'T>, ?token: System.Threading.CancellationToken) : Future<'T> =
nativeOnly

// [<ImportMember "dart:core">]
[<Global>]
let print (item: obj) : unit = nativeOnly
Expand Down
8 changes: 8 additions & 0 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,11 @@ let tryEntityIdent (com: Compiler) entFullName =
match entFullName with
| "Fable.Core.Dart.Future`1" -> makeIdentExpr "Future" |> Some
| "Fable.Core.Dart.Stream`1" -> makeIdentExpr "Stream" |> Some
| "Microsoft.FSharp.Control.FSharpAsync`1" -> makeImportLib com MetaType "Async" "AsyncBuilder" |> Some
| "System.Threading.CancellationToken"
| "System.Threading.CancellationTokenSource" ->
makeImportLib com MetaType "CancellationToken" "AsyncBuilder" |> Some
| "System.Threading.CancellationTokenRegistration" -> makeImportLib com MetaType "IDisposable" "Types" |> Some
| BuiltinDefinition BclDateOnly
| BuiltinDefinition BclDateTime
| BuiltinDefinition BclDateTimeOffset -> makeIdentExpr "DateTime" |> Some
Expand Down Expand Up @@ -715,6 +720,8 @@ let tryCoreOp com r t coreModule coreMember args =
let fableCoreLib (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.DeclaringEntityFullName, i.CompiledName with
| _, UniversalFableCoreHelpers com ctx r t i args error expr -> Some expr
| _, "Async.AwaitFuture.Static" -> Helper.LibCall(com, "Async", "awaitFuture", t, args, ?loc = r) |> Some
| _, "Async.StartAsFuture.Static" -> Helper.LibCall(com, "Async", "startAsFuture", t, args, ?loc = r) |> Some
| "Fable.Core.Reflection", meth -> Helper.LibCall(com, "Reflection", meth, t, args, ?loc = r) |> Some
| "Fable.Core.Compiler", meth ->
match meth with
Expand Down Expand Up @@ -4030,6 +4037,7 @@ let private replacedModules =
"System.Random", random
"System.Threading.CancellationToken", cancels
"System.Threading.CancellationTokenSource", cancels
"System.Threading.CancellationTokenRegistration", disposables
"System.Threading.Monitor", monitor
"System.Activator", activator
"System.Text.Encoding", encoding
Expand Down
269 changes: 269 additions & 0 deletions src/fable-library-dart/Async.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
import 'dart:async' as dart_async;

import 'AsyncBuilder.dart' as async_builder;
import 'Choice.dart' as choice;
import 'Types.dart' as types;

void _emptyContinuation<T>(T _value) {}

async_builder.Async<T> _invokeAsyncFunction<U, T>(Function function, U value) {
if (function is async_builder.Async<T> Function()) {
return function();
}

return (function as async_builder.Async<T> Function(U))(value);
}

async_builder.Async<T> makeAsync<T>(async_builder.Async<T> body) {
return body;
}

void invoke<T>(
async_builder.Async<T> computation,
async_builder.IAsyncContext<T> ctx,
) {
computation(ctx);
}

void callThenInvoke<T, U>(
async_builder.IAsyncContext<T> ctx,
U result1,
Function part2,
) {
_invokeAsyncFunction<U, T>(part2, result1)(ctx);
}

void bind<T, U>(
async_builder.IAsyncContext<T> ctx,
async_builder.Async<U> part1,
Function part2,
) {
async_builder.protectedBind<U, T>(part1, part2)(ctx);
}

Duration _cancellationDelay(Object? value) {
if (value is Duration) {
return value;
}

if (value is num) {
return Duration(milliseconds: value.toInt());
}

throw ArgumentError.value(
value,
'value',
'Expected Duration or milliseconds.',
);
}

async_builder.CancellationToken createCancellationToken([Object? arg]) {
final token = async_builder.CancellationToken(arg is bool ? arg : false);

if (arg is Duration || arg is num) {
token.cancelAfter(_cancellationDelay(arg));
}

return token;
}

void cancel(async_builder.CancellationToken token) {
token.cancel();
}

void cancelAfter(async_builder.CancellationToken token, Object delay) {
token.cancelAfter(_cancellationDelay(delay));
}

bool isCancellationRequested(async_builder.CancellationToken? token) {
return token?.isCancelled ?? false;
}

void throwIfCancellationRequested(async_builder.CancellationToken? token) {
if (token != null && token.isCancelled) {
throw async_builder.OperationCanceledException();
}
}

async_builder.Async<async_builder.CancellationToken> cancellationToken() {
return async_builder.protectedCont((ctx) => ctx.onSuccess(ctx.cancelToken));
}

final defaultCancellationToken = async_builder.CancellationToken();

async_builder.Async<choice.FSharpChoice$2<T, dynamic>> catchAsync<T>(
async_builder.Async<T> work,
) {
return async_builder.protectedCont<choice.FSharpChoice$2<T, dynamic>>((ctx) {
work(
async_builder.IAsyncContext<T>(
onSuccess: (value) {
ctx.onSuccess(choice.Choice_makeChoice1Of2<T, dynamic>(value));
},
onError: (error) {
ctx.onSuccess(choice.Choice_makeChoice2Of2<dynamic, T>(error));
},
onCancel: ctx.onCancel,
cancelToken: ctx.cancelToken,
trampoline: ctx.trampoline,
),
);
});
}

async_builder.Async<T> fromContinuations<T>(
void Function(async_builder.Continuations<T>) function,
) {
return async_builder.protectedCont<T>((ctx) {
function(
types.Tuple3<
async_builder.Continuation<T>,
async_builder.Continuation<dynamic>,
async_builder.Continuation<async_builder.OperationCanceledException>
>(ctx.onSuccess, ctx.onError, ctx.onCancel),
);
});
}

async_builder.Async<void> ignore<T>(async_builder.Async<T> computation) {
return async_builder.protectedBind<T, void>(
computation,
(_) => async_builder.protectedReturn<void>(null),
);
}

void start<T>(
async_builder.Async<T> computation, [
types.Some<async_builder.CancellationToken>? cancellationToken,
]) {
startWithContinuations<T>(
computation,
_emptyContinuation<T>,
(dynamic error) {
throw error;
},
_emptyContinuation<async_builder.OperationCanceledException>,
cancellationToken,
);
}

void startImmediate<T>(
async_builder.Async<T> computation, [
types.Some<async_builder.CancellationToken>? cancellationToken,
]) {
start<T>(computation, cancellationToken);
}

void startWithContinuations<T>(
async_builder.Async<T> computation,
Function continuation,
async_builder.Continuation<dynamic> exceptionContinuation,
async_builder.Continuation<async_builder.OperationCanceledException>
cancellationContinuation, [
types.Some<async_builder.CancellationToken>? cancelToken,
]) {
final trampoline = async_builder.Trampoline();

async_builder.Continuation<T> doneSuccess(Function cont) {
return (value) {
trampoline.completed = true;

if (cont is void Function()) {
cont();
} else {
(cont as void Function(T))(value);
}
};
}

async_builder.Continuation<U> done<U>(async_builder.Continuation<U> cont) {
return (value) {
trampoline.completed = true;
cont(value);
};
}

computation(
async_builder.IAsyncContext<T>(
onSuccess: doneSuccess(continuation),
onError: done<dynamic>(exceptionContinuation),
onCancel: done<async_builder.OperationCanceledException>(
cancellationContinuation,
),
cancelToken: cancelToken?.value ?? defaultCancellationToken,
trampoline: trampoline,
),
);
}

async_builder.Async<void> sleep(Object delay) {
return async_builder.protectedCont<void>((ctx) {
late final dart_async.Timer timer;
int? listenerId;
var completed = false;

timer = dart_async.Timer(_cancellationDelay(delay), () {
if (completed) {
return;
}

completed = true;

if (listenerId != null) {
ctx.cancelToken.removeListener(listenerId!);
}

ctx.onSuccess(null);
});

listenerId = ctx.cancelToken.addListener(() {
if (completed) {
return;
}

completed = true;
timer.cancel();
ctx.onCancel(async_builder.OperationCanceledException());
});
});
}

async_builder.Async<T> awaitFuture<T>(dart_async.Future<T> future) {
return async_builder.protectedCont<T>((ctx) {
future.then<void>(
(value) {
ctx.onSuccess(value);
},
onError: (Object error, StackTrace stackTrace) {
if (error is async_builder.OperationCanceledException) {
ctx.onCancel(error);
} else {
ctx.onError(error);
}
},
);
});
}

dart_async.Future<T> startAsFuture<T>(
async_builder.Async<T> computation, [
types.Some<async_builder.CancellationToken>? cancellationToken,
]) {
final completer = dart_async.Completer<T>();

startWithContinuations<T>(
computation,
(value) {
completer.complete(value);
},
(dynamic error) {
completer.completeError(error);
},
(error) {
completer.completeError(error);
},
cancellationToken,
);

return completer.future;
}
Loading
Loading