首页 > 代码库 > [JS Compose] 3. Use chain for composable error handling with nested Eithers (flatMap)

[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))

 

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