[JS Compose] 3. Use chain for composable error handling with nested Eithers (flatMap)

本文介绍如何使用函数式编程概念Either和chain来替代传统的try/catch块,实现错误处理的扁平化和链式调用,提高代码的可读性和维护性。

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!

转载于:https://www.cnblogs.com/Answer1215/p/6175746.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值