noway - PUBLIC
  • DOCS FOR THINGS
  • _ref
    • Working Memory
    • NASA’s 10 rules for writing mission-critical code
    • React Native
  • _docs
    • Cryptography
    • Store APIs Key in FrontEnd SDK issues
    • APIs abuse issues
    • Webpack
    • Specific things
      • Text recognition
    • Book reviews
      • April 2020
      • 1-2/2020
      • March 2020
    • Visualization
    • MacOS
    • Go
    • Server
      • Easy way to setup a local HTTP Server on macOS
    • React
    • Reading
    • Tips
      • MVP
      • Sleeping in the right way
      • Get more things done
    • VS Code
      • Intelligent doesn't auto complete the JSX attribute or react-native components
      • Setting.json
    • Service Registration
      • Apple Enterprise Developer Account
    • DevOps
      • Using NGINX for ReactJS
    • Firebase
      • Upload image
      • Save data
      • Save Data with unique key
      • Setup local firebase functions
    • Documentation
      • API Documentation
    • React Native
      • Tips
      • Performance
      • Patterns in React
      • Rendering in React
      • View JS from apk
      • Flatlist with large data list causes blank view
      • Facebook SDK
      • Screen Events
      • Hotkeys
      • Deep Links
        • iOS Deep Link: Universal Link vs URL Schemes‌
        • Deferred Link‌
      • Text going off screen with flexDirection: 'row'
  • _posts
    • 2020-07-13-How-to-Understand-things
    • 2019-02-20-how-to-create-Jekyll-blog-with-github-page
    • 2019-03-01-books-02-19
    • 2019-02-11-highlight-the-pragmatic-programmer-part-01
    • 2019-02-15-highlight-the-pragmatic-programmer-part-03
    • 2019-02-14-highlight-the-pragmatic-programmer-part-02
    • 2019-04-11-books-03-19
    • 2019-03-07-telegram-bot-as-a-real-time-logger
    • 2019-02-04-generic-loading-ios
    • 2019-01-31-summary-books-01-19
    • 2019-03-27-short-polling-with-Swift
    • 2019-02-22-try-catch-exception-rather-than-return-codes
    • 2019-09-29-api-docs
  • category-archive
  • tag-archive
Powered by GitBook
On this page

Was this helpful?

  1. _posts

2019-02-22-try-catch-exception-rather-than-return-codes

Previous2019-03-27-short-polling-with-SwiftNext2019-09-29-api-docs

Last updated 5 years ago

Was this helpful?

In a few weeks ago, I rarely used try-catch. But after I read again, I realised something and used try-catch more frequently.

Let's consider a scenario: a program has an user's information screen with usename, avatar field. After user changed [username, avatar], then press UPDATE button. The program will do:

  1. Validate username (longer than 5 characters).

  2. Show loading animation

  3. Upload avatar to an image server.

  4. Update user's info: username, avatar.

  5. Show Success alert, hide loading animation, .

  6. Hide loading animation if fail at any point.

Go with these codes (just focus on validateUsername and onUpdateButtonPressed function):

  const MIN_USERNAME_LENGTH = 6;
  const ERROR_TYPE = {
    NONE: 'NONE',
    NULL_USERNAME: 'NULL_USERNAME',
    SHORT_USERNAME: 'SHORT_USERNAME',
    UPLOAD_AVATAR: 'UPLOAD_AVATAR',
    UPDATE_INFO: 'UPDATE_INFO',
  }

  const SUCCESSFUL_UPDATED = 'Niceeee!';
  const NULL_USERNAME = 'Username can not be null';
  const SHORT_USERNAME = 'You are too short!';
  const FAIL_UPLOAD_AVATAR = 'Uploading avartar failed';
  const FAIL_UPDATE_INFO = 'Updating info failed';

  functions showAlert(contentText) {
    // show alert with contentText
  }

  function showSuccessAlert() {
    showAlert(SUCCESSFUL_UPDATED);
  }

  function showFailAlert(failText) {
    showAlert(failText);
  }

  function showLoading() {
    // show loading animation
  }

  function hideLoading() {
    // hide loading animation
  }

  function uploadImages(imagePath) {
    return new Promise((resolve, reject) => {
      Client.uploadImage(
        imagePath,
        () => { reject(ERROR_TYPE.UPLOAD_AVATAR); }, // error
        (url) => { resolve(url); }, // success
      );
    });
  }

  function updateUserInfo(username, avatarUrl) {
    return new Promise((resolve, reject) => {
      Client.updateUserInfo(
        username,
        avatarUrl,
        () => { reject(ERROR_TYPE.UPDATE_INFO); }, // error
        () => { resolve(); }, // success
      );
    });
  }

  function validateUsername(username) {
    if (!username) { 
      return ERROR_TYPE.NULL_USERNAME;
    }
    else if (username.length < MIN_USERNAME_LENGTH) {
      return ERROR_TYPE.SHORT_USERNAME;
    }
    return ERROR_TYPE.NONE;
  }

  function onUpdateButtonPressed(username, avatarPath) {

    // 1. validate username
    const resultValidateUsername = validateUsername(username);
    if (resultValidateUsername === ERROR_TYPE.NULL_USERNAME) {
      showFailAlert(NULL_USERNAME);
      return;
    }
    if (resultValidateUsername === ERROR_TYPE.SHORT_USERNAME) {
      showFailAlert(SHORT_USERNAME);
      return;
    }

    // 2. Show loading animation 
    showLoading();

    // 3. upload avatar
    uploadImages(avatarPath)
      .then(avatarUrl => {

        // 4. update user's info
        return updateUserInfo(username, avatarUrl);
      })
      .then(() => {

        // 5. hide loading and show success alert
        showSuccessAlert();
        hideLoading();
      })
      .catch((err) => {
        // 6. hide loading if failed
        hideLoading();

        if (err === ERROR_TYPE.UPLOAD_AVATAR) {
          showFailAlert(FAIL_UPLOAD_AVATAR);
        }
        else if (err === ERROR_TYPE.UPDATE_INFO){
          showFailAlert(FAIL_UPDATE_INFO);
        }
      });
  }

We can smell something in the onUpdateButtonPressed function:

  • 2 return lines(better than nested if)

  • chaining Promises(better than nested Promises)

  • a little complex show/hide loading flow

  • hard to figure out the updating flow

It's not bad at all. But imagine, if we have more error codes and chaining Promises, the code will be massive and hard to be maintained.

So let's refactor onUpdateButtonPressed with try-catch and async/await

  function validateUsername(username) {
    if (!username) { 
      throw ERROR_TYPE.NULL_USERNAME;
    }
    else if (username.length < MIN_USERNAME_LENGTH) {
      throw ERROR_TYPE.SHORT_USERNAME;
    }
  }

  async function onUpdateButtonPressed(username, avatarPath) {
    try {
      validateUsername(username);
      showLoading();
      const avatarUrl = await uploadImages(avatarPath);
      await updateUserInfo(username, avatarUrl);
      showSuccessAlert();
    }
    catch (err) {
      handleError(err);
    }
    finally {
      hideLoading();
    }
  }

  function handleError(err) {
    switch (err) {
      case ERROR_TYPE.NULL_USERNAME: 
        showFailAlert(NULL_USERNAME);
        break;
      case ERROR_TYPE.SHORT_USERNAME: 
        showFailAlert(SHORT_USERNAME);
        break;
      case ERROR_TYPE.UPLOAD_AVATAR: 
        showFailAlert(FAIL_UPLOAD_AVATAR);
        break;
      case ERROR_TYPE.UPDATE_INFO: 
        showFailAlert(FAIL_UPLOAD_AVATAR);
        break;
      default:
      break;
    }
  }

What I did:

  • In validateUsername, instead of return error codes, throw them.

  • In onUpdateButtonPressed, instead of using then/catch with Promises, using try-catch and async/await.

I think the code is good now:

  • We can easy understand the flow line by line: validateUsername -> showLoading -> uploadImages -> updateUserInfo -> showSuccessAlert

  • Errors are handled in one place handleError.

From now, I always try to use try-catch-finally first for cases as this scenario.

HAPPY CODING!

More detail about

Clean Code
try-catch