Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ms #49

Open
webfansplz opened this issue Oct 25, 2020 · 0 comments
Open

ms #49

webfansplz opened this issue Oct 25, 2020 · 0 comments

Comments

@webfansplz
Copy link
Owner

仓库:

ms -Tiny milisecond conversion utility

源码实现:

/**
 * Helpers.
 */

var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var w = d * 7;
var y = d * 365.25; // why ? 见以下解析第1点

/**
 * Parse or format the given `val`.
 *
 * Options:
 *
 *  - `long` verbose formatting [false]
 *
 * @param {String|Number} val
 * @param {Object} [options]
 * @throws {Error} throw an error if val is not a non-empty string or a number
 * @return {String|Number}
 * @api public
 */

module.exports = function (val, options) {
  options = options || {};
  var type = typeof val;
  // 字符串类型且不等于'',走parse方法
  if (type === "string" && val.length > 0) {
    return parse(val);
  }
  // number类型 且 为一个有限数值
  else if (type === "number" && isFinite(val)) {
    // long选项? fmtLong: fmtShort
    return options.long ? fmtLong(val) : fmtShort(val);
  }
  // 类型错误报错
  throw new Error(
    "val is not a non-empty string or a valid number. val=" +
      JSON.stringify(val)
  );
};

/**
 * Parse the given `str` and return milliseconds.
 *
 * @param {String} str
 * @return {Number}
 * @api private
 */

// 字符串解析成数字
function parse(str) {
  str = String(str);
  // why? why 100? 见以下解析2,3点
  if (str.length > 100) {
    return;
  }
  // 基操正则..
  var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
    str
  );
  if (!match) {
    return;
  }
  var n = parseFloat(match[1]);
  var type = (match[2] || "ms").toLowerCase();
  switch (type) {
    case "years":
    case "year":
    case "yrs":
    case "yr":
    case "y":
      return n * y;
    case "weeks":
    case "week":
    case "w":
      return n * w;
    case "days":
    case "day":
    case "d":
      return n * d;
    case "hours":
    case "hour":
    case "hrs":
    case "hr":
    case "h":
      return n * h;
    case "minutes":
    case "minute":
    case "mins":
    case "min":
    case "m":
      return n * m;
    case "seconds":
    case "second":
    case "secs":
    case "sec":
    case "s":
      return n * s;
    case "milliseconds":
    case "millisecond":
    case "msecs":
    case "msec":
    case "ms":
      return n;
    default:
      return undefined;
  }
}

/**
 * Short format for `ms`.
 *
 * @param {Number} ms
 * @return {String}
 * @api private
 */
// 直接相除四舍五入取整
function fmtShort(ms) {
  var msAbs = Math.abs(ms);
  if (msAbs >= d) {
    return Math.round(ms / d) + "d";
  }
  if (msAbs >= h) {
    return Math.round(ms / h) + "h";
  }
  if (msAbs >= m) {
    return Math.round(ms / m) + "m";
  }
  if (msAbs >= s) {
    return Math.round(ms / s) + "s";
  }
  return ms + "ms";
}

/**
 * Long format for `ms`.
 *
 * @param {Number} ms
 * @return {String}
 * @api private
 */
// long选项其实就是单位描述更加详细完整
function fmtLong(ms) {
  var msAbs = Math.abs(ms);
  if (msAbs >= d) {
    return plural(ms, msAbs, d, "day");
  }
  if (msAbs >= h) {
    return plural(ms, msAbs, h, "hour");
  }
  if (msAbs >= m) {
    return plural(ms, msAbs, m, "minute");
  }
  if (msAbs >= s) {
    return plural(ms, msAbs, s, "second");
  }
  return ms + " ms";
}

/**
 * Pluralization helper.
 */

// 复数判断
function plural(ms, msAbs, n, name) {
  // 复数判断,因为Math.round是四舍五入取整,所以此处判断使用n*1.5
  var isPlural = msAbs >= n * 1.5;
  // ms(89999, { long: true }) 1 minute
  // ms(90000, { long: true }) 2 minutes
  return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
}

解析:

  1. Why are there 365.25 days in a year?

  2. Limit str to 100 to avoid ReDoS of 0.3s

  3. By limiting the input length It prevents the regular expression that does the parsing from consuming too much cpu time blocking the event loop

收获:

其实 ms 的源码非常精简,也非常易读,但是我却从它的 History Commits 榨出了一些知识点:

第一版的 ms 代码其实是更简单的,当然了也存在一些问题。后面随着迭代和一些大佬(我在这里也看到了 tj 大佬,tj 大佬真是无处不在啊...)的 pr 贡献,发生了以下变化:

  • 更好的可读性 (函数/变量命名)
  • 更强的易用性 (unit 参数更多样化,暴露 long 参数)
  • 更严谨的边界判断及优化 (见上面 2,3 点解析)
  • 抛出异常 (哈哈,一开始的几个版本,没做参数的类型校验异常错误抛出)

看源码,真的也能从它的 History Commits 学到很多~

100 多行,2kb 多的库,却有 3.2k 的 star,足已见得它的实用与易用。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant