剛進入公司開發了兩塊代碼,遇到了負責的老大,開始了規範的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很沒有用,這一次收穫真的挺大的,還有不少偏業務的問題不方便總結出來,還有不少沒有特色的問題也沒有列出來,此次開發還學會了寫測試用例。
敲代碼有時候不是爲了實現需求。更像是打磨工藝品,秉承工匠精神,你的態度決定了你代碼的質量。感謝大佬教給本身的,相信之後也會學到不少,本身的代碼質量也會蹭蹭蹭往上提。
來自一個踏上職場兩週的孩子的感悟
把這些分享給你但願與你一塊兒進步 仍是至關的爽這個代碼敲着就很是的舒服了