刚进入公司开发了两块代码,遇到了负责的老大,开始了规范的pr和codereview学到了一些东西,和你们分享总结一下。javascript
这应该算是修改的比较多的问题了,举几个比较典型的栗子:java
大概是以下的一段抛错,其实就是在数据库里查一个东西没有查到嘛数据库
throw new Error('Did not find the package whose ID is 2.')
复制代码
下面是和大佬的一段对话:(大佬:D,垃圾:L)api
无论说是英语很差仍是语文很差,其实归结于本身没有多思考一下,没有作到那种精益求精的工匠精神。我相信没有人能说我那句话错了,但事实就是它很差。promise
// 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
下面是错误的写法,大佬看了一眼说不对,让改。郁闷死我了,我花了好长时间去试验最终证实了,个人写法在解析的时候确实会出现乱码。贴代码异步
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);
});
复制代码
真的有乱码了!
错误: 在这里真的出现乱码了,下面来分析一下缘由。
一个英文字符占用一个字节,而一个汉字占用三个字节。当文件较大须要进行切割的时候。
假设:每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);
});
复制代码
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)
复制代码
每一秒执行一次函数,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();
}
}
复制代码
完美解决问题
关于异常捕获被老大点了两次名:
因而本身默默研究了一下,什么地方能捕获到异常,何时捕获不到,应该在哪些地方捕获异常,是否是每个请求都应该try...catch?
下面与你们简单分享一下很是low的捕获经验,我知道有不少更好的捕获模式,例如我就特别喜欢egg的异常处理,在service层controller层的错误都通过包装,把错误抛出来,经过中间件进行统一处理。
关于这一块,整理学习了一些,但确定还远远不够,慢慢积累吧这一块的经验。但如今起码能够用最少的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
复制代码
三个版本能够说变化很是的大
这一次变化是我本身就想作的,由于在开发过程当中我就感觉到,可用性太差,每次都须要输入太多东西。总的来讲这一次变化就是为了***让用户减小输入的内容***。
矛盾和收获都发生在这一次。
个人观点: -o 输出本地文件 -d部署 --prod 描述环境 --pv版本相关操做,一个指令一个动做,很是清晰。要干什么事儿就用什么指令,不该该再简化了,逻辑很是清晰。 可以省去的输入项我都已经去掉了。
大佬的观点: 再简化,简化到极致(以至于我认为会对命令动做描述不清楚),大佬认为我说的我那样的清晰的逻辑是针对开发者的,不能站在一个技术的角度,以为这样实现起来逻辑清晰。注意了划重点了 我一直欺骗本身的逻辑清晰原来是本身实现起来逻辑清晰。(恍然大悟)
因而第三次进行了大改造,将命令简化到了缺一不可。这两次是对代码的改动是最大的,但也是我这一次收获最大的,学会了什么叫人性化,什么叫站在用户的角度思考问题,忘掉本身是一个开发者。
在过去一直以为codereview很没有用,这一次收获真的挺大的,还有不少偏业务的问题不方便总结出来,还有不少没有特色的问题也没有列出来,此次开发还学会了写测试用例。
敲代码有时候不是为了实现需求。更像是打磨工艺品,秉承工匠精神,你的态度决定了你代码的质量。感谢大佬教给本身的,相信之后也会学到不少,本身的代码质量也会蹭蹭蹭往上提。
来自一个踏上职场两周的孩子的感悟
把这些分享给你但愿与你一块儿进步 仍是至关的爽这个代码敲着就很是的舒服了