codereview后的收获

Abstract

刚进入公司开发了两块代码,遇到了负责的老大,开始了规范的pr和codereview学到了一些东西,和你们分享总结一下。javascript

命名规范和表达问题

这应该算是修改的比较多的问题了,举几个比较典型的栗子:java

1. 表达问题

大概是以下的一段抛错,其实就是在数据库里查一个东西没有查到嘛数据库

throw new Error('Did not find the package whose ID is 2.')
复制代码

下面是和大佬的一段对话:(大佬:D,垃圾:L)api

  • D: 你英语是体育老师教的吧。。。哪有你这样写的。。。
  • L: 哇!就是个人英语是体育老师教的,我才专门在网上翻译的。
  • D: 怕是假的翻译软件哟。
  • L: 谷歌翻译有图有真相!

image

  • D: 你这是语文没学好(一脸无语,默默贴出他的谷歌翻译)

image

无论说是英语很差仍是语文很差,其实归结于本身没有多思考一下,没有作到那种精益求精的工匠精神。我相信没有人能说我那句话错了,但事实就是它很差。promise

2. API设计规范

// bad
get('/package/package_with_app_version?appid=xxx&version=xxx');
// good
get('/package?appid=xxx&version=xxx');
复制代码

通过多方查阅,关于api的设计规范,都比较推崇RESTful的API风格。bash

经过HTTP Method来描述动做服务器

Method 对应数据库行为 描述
GET SELECT 从服务器取出资源
POST CREATE 在服务器新建一个资源
PUT UPDATE 在服务器更新资源(客户端提供改变后的完整资源)
PATCH UPDATE 在服务器更新资源(客户端提供改变的属性)
DELETE DELETE 从服务器删除资源

example:网络

// 获取包列表
get('/package');
// 获取ID是12的包
get('/package/12');
// 严格遵照RESTFUL的话,应该是经过这两个参数筛选返回列表
// 但我以为在业务里不太合适,因而根据业务改成,直接返回对应的包
get('/package?appid=xxx&version=xxx');
// 若是想要严格遵照规范的话应该写成这样
get('/app/:appid/package/:version')
// 指定第几页,以及每页的记录数。
get('/package?page=2&per_page=100');
// 更新包,客户端提供包的全部信息,相似于替换
put('/package/:id');
// 更新包,客户端提供须要更新的信息
patch('/package/:id');
// 删除包
delete('/package/:id');
// 建立包
post('/package');
复制代码

更规范的设计API,能够一目了然的了解这个API将会发生的动做。app

代码错误

1. 在读取文件拼接buffer的时候出现的问题

下面是错误的写法,大佬看了一眼说不对,让改。郁闷死我了,我花了好长时间去试验最终证实了,个人写法在解析的时候确实会出现乱码。贴代码异步

const fs = require('fs');
let rs = fs.createReadStream('./Zn-utf8.txt');
let body = '';
rs.on("data", (chunk) => {
  body += chunk;
});
rs.on("end", () => {
  console.log(body);
});
复制代码

image

真的有乱码了!

错误: 在这里真的出现乱码了,下面来分析一下缘由。

一个英文字符占用一个字节,而一个汉字占用三个字节。当文件较大须要进行切割的时候。

假设:每17个字符进行一次切割,在读取

'一个汉字占用三个字节的时候'
==>
'一(1,2,3)个(4,5,6)汉(7,8,9)字(10,11,12)三(13,14,15)个(16,17,18)字(19,20,21)节(22,23,24)'
==>
chunk1:'一个汉字三??'+chunk2:'?字节'
复制代码

而在上面代码中拼接字符串的时候,出现了隐式的toString(),把不完整的chunk转成了字符串,致使不完整的汉字不能正确解析出现乱码。

这就是为何会出现那三个乱码的缘由

解决方法:

let body = [];
rs.on('data', (chunk) => {
  body.push(chunk);
});
rs.on('end', () => {
  body = Buffer.concat(body).toString();
  console.log(body);
});
复制代码

2. 使用定时器检查状态

let status = await checkStatus();
if (status === 'success' || status === 'faild') {
  if (status === 'success') {
    console.log('成功!接下来该作啥作啥。')
  } else if (status === 'faild') {
    console.error('失败!想干啥都不行。');
  }
} else {
  this.statusTimer = setInterval(async () => {
    status = await checkStatus();
    if (status === 'success' || status === 'faild') {
      clearInterval(this.statusTimer);
      if (status === 'success') {
        console.log('成功!接下来该作啥作啥。')
      } else if (status === 'faild') {
        console.error('失败!想干啥都不行。');
      }
    }
  }, 200);
}
复制代码

大概的逻辑就是,发一个异步请求去检查状态checkStatus(),可能会有三个状态:success、faild和pending;若是是pending状态,就会开启一个setInterval,每200毫秒检查一次,知道产生结果为止。

听起来好像没有什么问题,我最初也是这样考虑的,但是结果确出乎个人意料,由于在本地调试,请求时间短到没有发现这个问题,大佬一眼就看出来了,放一段代码和运行结果,你们就一目了然。

// 概念版的上面那段逻辑
var timer = setInterval(async () => {
	console.log('发送请求以前作的事')
    await new Promise(resolve => setTimeout(resolve, 5000));
    console.log('收到请求以后作的事');
 }, 1000)
复制代码

image

每一秒执行一次函数,await会阻塞函数后面的内容五秒,用clearInterval清掉定时器,表明收到了想要的结果,再也不执行函数。然而还有五次‘收到请求以后作的事’被输出出来。

也就是说当网络状况很差的时候,我以前的代码会形成,检查到状态是成功之后清调了定时器之后,极可能还有数次函数没有执行完,会屡次检查到状态成功,触发成功之后干的事情,形成严重后果。

优化方案,借助递归+sleep函数解决问题:

用sleep也只是为了减小无效的请求,主要解决问题的仍是递归。

async function checkStatus() {
  const status = await checkStatus();
  if (status === 'success' || status === 'faild') {
    if (status === 'success') {
      console.log('成功!接下来该作啥作啥。')
    } else if (status === 'faild') {
      console.error('失败!想干啥都不行。');
    }
  } else {
    console.log('pending...');
    await new Promise(resolve => setTimeout(resolve, 500));
    await checkStatus();
  }
}
复制代码

完美解决问题

异常捕获

关于异常捕获被老大点了两次名:

  1. 你的请求都不try...catch一下的,抛错了怎么捕获?
  2. 我改了之后>>>>>>你怎么处处都在try...catch写着好玩么?

因而本身默默研究了一下,什么地方能捕获到异常,何时捕获不到,应该在哪些地方捕获异常,是否是每个请求都应该try...catch?

下面与你们简单分享一下很是low的捕获经验,我知道有不少更好的捕获模式,例如我就特别喜欢egg的异常处理,在service层controller层的错误都通过包装,把错误抛出来,经过中间件进行统一处理。

  • Q:何时捕获不到?
  • A:在JS中,对于异步回调函数,trycatch就无能为力。你捕获不到,就只有被原生的uncaughtException捕获到全部没有被捕获的错误,在这时就不能作合适的错误处理了。
  • Q:应该如何捕获异步函数?
  • A:说白了只要不跳出了异步调用链就能捕获。解决方法有不少例如借用promise将异步的错误reject捕获。或者借助async await,也能够轻易的捕获到异常。
  • Q:是否是全部地方都应该捕获异常呢?
  • A:答案固然是否是的。这个尺度本身把我,异常会向上冒,在里层throw出的异常,在往上冒的过程当中就会上层的catch捕获,因此确定不须要处处都写。将异常抛出,到你以为应该输出错误的时候就输出错误,须要对错误进行处理的时候再捕获,这个尺度我可能还须要更多的经验去感觉。

关于这一块,整理学习了一些,但确定还远远不够,慢慢积累吧这一块的经验。但如今起码能够用最少的try...catch,把错误都处理了。

学会站在用户的角度思考问题

关于这一点和代码无关和程序无关和逻辑无关,就是一个视角问题。

下面是开发过程当中对CLI命令的设计

初版

# 在本地生成
seeder-package [packageName] -o ./dist/package.tgz
# 部署测试环境(自动上线)
seeder-package [packageName] -d ./dist/package.tgz
# 部署生产环境(须要手动上线)
seeder-package [packageName] -d ./dist/package.tgz -e production
# 更新版本号
seeder-package [packageName] --pv major
seeder-package [packageName] --pv minor
seeder-package [packageName] --pv patch
seeder-package [packageName] --pv 3.2.5
# 查看当前版本号
seeder-package [packageName] --pv current
复制代码

第二版

# 在本地生成
seeder [packageName] -o ./dist/package.tgz
# 把本地指定路径的包部署到测试环境
seeder [packageName] -d ./dist/package.tgz
# 打包并部署到测试环境
seeder [packageName] -d -o
# 在测试环境拉最新版本的包 部署到生产环境
seeder [packageName] -d --prod
# 更新版本 打包 部署测试 上线
seeder [packageName] -o -d --pv [xxx]
# 更新版本 打包 部署测试 上线 的简写
seeder [packageName]
复制代码

第三版

# 输出本地文件
seeder [packageName] -o xxx/xxx.tgz
# 打包部署并更新 patch 版本号
seeder [packageName]
# 打包部署并更新指定版本号
seeder [packageName] --pv [xxx]
# 同步到生产
seeder [packageName] -p
复制代码

三个版本能够说变化很是的大

初版 ==> 第二版

这一次变化是我本身就想作的,由于在开发过程当中我就感觉到,可用性太差,每次都须要输入太多东西。总的来讲这一次变化就是为了***让用户减小输入的内容***。

  • 将seeder-package指令 缩短为 seeder
  • 将须要主动申明部署环境,-e 环境的默认值为测试环境,只有生产环境的时候才须要设置。
  • 将打包部署两个命令合并,而且不须要传后面长长的参数。
  • 相似于 -o -d --pv [xxx]这样的指令能够同时完成几个工做。
  • 作出了简写

第二版 ==> 第三版

矛盾和收获都发生在这一次。


个人观点: -o 输出本地文件 -d部署 --prod 描述环境 --pv版本相关操做,一个指令一个动做,很是清晰。要干什么事儿就用什么指令,不该该再简化了,逻辑很是清晰。 可以省去的输入项我都已经去掉了。


大佬的观点: 再简化,简化到极致(以至于我认为会对命令动做描述不清楚),大佬认为我说的我那样的清晰的逻辑是针对开发者的,不能站在一个技术的角度,以为这样实现起来逻辑清晰。注意了划重点了 我一直欺骗本身的逻辑清晰原来是本身实现起来逻辑清晰。(恍然大悟)


因而第三次进行了大改造,将命令简化到了缺一不可。这两次是对代码的改动是最大的,但也是我这一次收获最大的,学会了什么叫人性化,什么叫站在用户的角度思考问题,忘掉本身是一个开发者。

感谢

在过去一直以为codereview很没有用,这一次收获真的挺大的,还有不少偏业务的问题不方便总结出来,还有不少没有特色的问题也没有列出来,此次开发还学会了写测试用例。

敲代码有时候不是为了实现需求。更像是打磨工艺品,秉承工匠精神,你的态度决定了你代码的质量。感谢大佬教给本身的,相信之后也会学到不少,本身的代码质量也会蹭蹭蹭往上提。


来自一个踏上职场两周的孩子的感悟

把这些分享给你但愿与你一块儿进步 仍是至关的爽这个代码敲着就很是的舒服了

相关文章
相关标签/搜索