微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 F# 中异步下载网页并捕获错误

如何解决如何在 F# 中异步下载网页并捕获错误

我一直在尝试找出惯用的 F# 方式来异步下载网页并处理任何错误/HTTP 失败代码,我认为这是我迄今为止最接近的尝试,但我在 Choice1Of2 行上遇到了类型错误

我愿意

a) 理解为什么会失败(我还在学习 F#)
b) 知道这是否是正确的方法/如何进行这项工作,或者我是否完全走错了路

error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C2146: Syntax error: missing ';' before identifier 'SEGMENT'
import 'package:Flutter/material.dart';
import 'dart:math';

void main() {
  return runApp(
    MaterialApp(home: DicePage()),);
}

class DicePage extends StatefulWidget {
  @override
  _DicePageState createState() => _DicePageState();
}

class _DicePageState extends State<DicePage> {
  int leftDiceNum = 1;
  int rightDiceNum = 1;

  diceRandomizer() {
    setState(() {
      leftDiceNum = Random().nextInt(6) + 1;
      rightDiceNum = Random().nextInt(6) + 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white70,appBar: AppBar(
          centerTitle: true,title: Text('Dices'),backgroundColor: Colors.teal),body: Center(
        child: Row(
          children: <Widget>[
            Expanded(
              child: TextButton(
                onpressed: () {
                  diceRandomizer();
                },child: Image.asset('images/dice$leftDiceNum.png'),),Expanded(
              child: TextButton(
                onpressed: () {
                  diceRandomizer();
                },child: Image.asset('images/dice$rightDiceNum.png'),],floatingActionButton: Row(
        children: [
          FloatingActionButton(
            onpressed: () {diceRandomizer();},FloatingActionButton(
            onpressed: (){diceRandomizer();},)
        ],);
  }
}

解决方法

a) 失败的原因

您的 fetchAsync 函数返回一个 Async<Choice<_>>,并且您的模式匹配就好像该函数只返回 Choice<_> 一样。不幸的是,您不能在 Async 上进行模式匹配,因为这将是一个阻塞操作,而这正是 Async 尝试远离的内容。

b) 处理这些事情的惯用方式。

但是,您可以做的是留在异步上下文中并处理其中的故障。 F# 提供(至少)两种常见的方法来处理这个问题。例如。您可以使用允许您编写管道的异步辅助函数:

let fetchAsyncPipeline (url: string) =
    FSharp.Data.Http.AsyncRequestString(url)
    |> Async.Catch
    |> Async.map (function
        | Choice1Of2 v -> Ok v
        | Choice2Of2 e -> Error e.Message)

不幸的是,Async.map 包含在 not yet 中。但是你可以像这样为自己定义:

namespace global

[<RequireQualifiedAccess>]
module Async =

    let map f xA =
        async {
            let! x = xA
            return f x
        }

或者,您可以使用 F# 的 computation expressions 提供语法糖,以更命令式的方式编写上述内容:

let fetchAsyncCe (url: string) =
    async {
        try
            return!
                FSharp.Data.Http.AsyncRequestString(url)
                |> Async.map Ok
            with
            | e -> return Error e.Message
    }

在这两种解决方案中,我都将异常转换为 F# 的 result 类型,我个人认为这是处理错误的最佳方式。

最后,如 brianberns 所示,您的表达式仅包含在 Async 类型中。但是unlike C#的Task,async计算代表了程序如何计算一些东西,但是那个程序还没有启动,你必须显式运行异步操作。一种方法是使用 Async.RunSynchronously:

Async.RunSynchronously (fetchAsyncCe "https://fsharpforfunandprofit.com/")

PS:您可能遇到过 Scott Wlaschin 出色的 F# for fun and profit,但如果没有,您应该查看一下。就个人而言,我发现它是教您 F# 和一般函数式编程的最佳资源。

,

这不编译的原因是因为您实际上从未运行过 fetchAsync 创建的计算。这样的事情应该可以工作:

let fetchAsync url =
    Http.AsyncRequestString(url)
        |> Async.Catch
        |> Async.RunSynchronously
,

几个小时后,喝了很多咖啡,我喝了这个

let fetchAsync2 (url: string) =
async { return! Async.Catch(Http.AsyncRequestString(url)) }
|> Async.RunSynchronously

let result = fetchAsync2 url

match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message

此代码按我希望的方式工作,但如果有人有更好的解决方案,我会将问题悬而未决

合并我的版本和@brianberns 提供的版本,我认为我最喜欢这个版本?

let fetchAsync3 (url: string) =
Http.AsyncRequestString(url)
|> Async.Catch 
|> Async.RunSynchronously

let result = fetchAsync3 url

match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。