zoukankan      html  css  js  c++  java
  • [JS Compose] 3. Use chain for composable error handling with nested Eithers (flatMap)

    We refactor a function that uses try/catch to a single composed expression using Either. We then introduce the chain function to deal with nested Eithers resulting from two try/catch calls.

    For example we have this code using try & catch:

    const getPort = () => {
      try {
        const file = fs.readFileSync('config.json');
        const c = JSON.parse(file);
        return c.port;
      } catch(e) {
        return 3000;
      }
    }

    And now, we want to use Either to rewirte the code:

    // SETUP: fake fs
    //==========
    const fs = {
      readFileSync: name => {
        if(name === 'config.json') {
          return JSON.stringify({port: 8888})
        } else {
          throw('missing file!')
        }
      }
    }
    
    //=================
    
    const Right = x => ({
        map: f => Right(f(x)),
        fold: (f, g) => g(x),
        toString: () => `Right(${x})`
      });
    
    const Left = x => ({
      map: f => Left(x),
      fold: (f, g) => f(x),
      toString: () => `Left(${x})`
    });
    
    const fromNullable = x => 
      x != null ? Right(x): Left(null);
    
    const tryCatch = f => {
      try {
        return Right(f());
      } catch(e) {
        return Left(e);
      }
    }
    
    //=========================
    const getPort = () =>
      tryCatch(() => fs.readFileSync('config.json'))
      .map(f => JSON.parse(f))
      .fold(
        x => 3000,
        x => x.port);
    
    console.log(getPort('config.json'))

    We wrote the function 'tryCatch', the idea is put the code need to be checked in try, when success call 'Right', error, call 'Left()'.

    tryCatch(() => fs.readFileSync('config.json'))

    Read the file, is success, will return file content. If not, then goes to set default 3000.

      .fold(
        x => 3000,
        x => x.port);

    It works, but we still miss one things, in the old code, the 'JSON.parse' are also wrapped into try catch.

    const getPort = () =>
      tryCatch(() => fs.readFileSync('config.json')) //Right('{port:8888}')
      .map(f => tryCatach(() => JSON.parse(f))) //Right(Right({port:8888}))

    But once we also wrap parseing code into tryCatch function, the return value is 2d-Right. So we want to flatten it.

    const Right = x => ({
        map: f => Right(f(x)),
        flatMap: f => f(x),
        fold: (f, g) => g(x),
        toString: () => `Right(${x})`
      });
    
    const Left = x => ({
      map: f => Left(x),
      flatMap: f => f(x),
      fold: (f, g) => f(x),
      toString: () => `Left(${x})`
    });

    We add 'flatMap', so instead of putting the value into Right() or Left(), we just return the value. Because we know the value passed in is already a Right or Left.

    const getPort = () =>
      tryCatch(() => fs.readFileSync('config.json')) //Right('{port:8888}')
      .flatMap(f => tryCatch(() => JSON.parse(f))) //Right({port:8888})
      .fold(
        x => 3000,
        x => x.port);

    ---------

    // SETUP: fake fs
    //==========
    const fs = {
      readFileSync: name => {
        if(name === 'config.json') {
          return JSON.stringify({port: 8888})
        } else {
          throw('missing file!')
        }
      }
    }
    
    //=================
    
    const Right = x => ({
        map: f => Right(f(x)),
        flatMap: f => f(x),
        fold: (f, g) => g(x),
        toString: () => `Right(${x})`
      });
    
    const Left = x => ({
      map: f => Left(x),
      flatMap: f => f(x),
      fold: (f, g) => f(x),
      toString: () => `Left(${x})`
    });
    
    const fromNullable = x => 
      x != null ? Right(x): Left(null);
    
    const tryCatch = f => {
      try {
        return Right(f());
      } catch(e) {
        return Left(e);
      }
    }
    
    //=========================
    const getPort = () =>
      tryCatch(() => fs.readFileSync('config.json')) //Right({port:8888})
      .flatMap(f => tryCatch(() => JSON.parse(f))) //Right(Right({port:8888}))
      .fold(
        x => 3000,
        x => x.port);
    
    console.log(getPort('config.json'))

    -----

    You can also rename 'flatMap' to 'chain'. Sometime 'flatMap' or 'chain' looks similar to 'fold' implementation. But the meaning is different, here 'chain / flatMap' says It says, "If we're going to return another Either, we are going to use chain instead of map." 'fold' says just get the value out of the box, it's done!

  • 相关阅读:
    5月12日上课笔记-js 弹出框、函数、程序调试、基本事件、浏览器对象模型
    5月11日上课笔记-js简介
    5月9日上课笔记-网页定位、网页动画【HTML5】
    用IO字节流复制文件-CopyFileByIo
    5月8日上课笔记-浮动float
    5月5日上课笔记-盒子模型【HTML5】
    @RequestParam,@PathParam,@PathVariable,@QueryParam注解的使用区别
    vue-router 的配置
    VSCode自定义代码片段(模板)
    使用通用 Mapper 依据数据表生成实体类
  • 原文地址:https://www.cnblogs.com/Answer1215/p/6175746.html
Copyright © 2011-2022 走看看