最近項目事情很多,開發過程中也碰到了很多值得記錄的問題,今天就早點來公司,把最近一些問題和經驗總結一下,以便以後進行參考。
JS數值精度丟失
這其實是一個衆所周知的問題,但他本身並不是js的bug,js採用的是IEEE 754 規範,採用雙精度存儲(double precision),佔用 64 bit。所以這個BUG並不是只有js有,只要是採用了這個規範的語言都會有這個問題。
JS 中能精準表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。所以當數值大於該值時就會出現精度丟失。在項目中後端傳給前端的ID是數值並且超過了最大數值,最後導致了精度的丟失,當時也是找了很久才發現是這個原因,對於這個問題前端並沒有很好的解決辦法,只能是由後端將數值轉爲字符串傳給前端解決問題。
async的使用
這是ES2017標準中引入的一個函數,其實並不是很新的東西,已經很多人在使用了,但是我一直還停留在使用promise的階段,這次項目中碰見了需要加載很多圖片,然後進行壓縮的情況,想起來用async應該會很方便,然後就用上了,代碼確實簡介了不少,增強了代碼的可讀性,比起回調和promise又更加方便了一點。
async函數返回一個 Promise 對象,可以使用then方法添加回調函數。當函數執行的時候,一旦遇到await就會先返回,等到異步操作完成,再接着執行函數體內後面的語句。任何一個await語句後面的 Promise 對象變爲reject狀態,那麼整個async函數都會中斷執行。
await命令後面的Promise對象,運行結果可能是rejected,所以最好把await命令放在try…catch代碼塊中。
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另一種寫法
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
});
多個await命令後面的異步操作,如果不存在繼發關係,最好讓它們同時觸發。
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
await命令只能用在async函數之中,如果用在普通函數,就會報錯。也就是說不能用在foreach函數中,可以用for循環代替。
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 報錯
docs.forEach(function (doc) {
await db.post(doc);
});
}
//正確寫法
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}
用上述的寫法發送請求所有遠程操作都是繼發。只有前一個 URL 返回結果,纔會去讀取下一個 URL,這樣做效率很差,非常浪費時間。我們需要的是併發發出遠程請求。項目裏需要請求上百張圖片,然後對返回的圖片壓縮成zip包,之前就是用上述的方法進行壓縮,效率真的很差,後面看到了阮一峯老師的寫法,頓時感覺大神就是不一樣。
async function compressionImage(imgUrlArray, id) {
const zip = new JSZip()
//併發發出遠程請求。
const result = imgUrlArray.map(async url => {
const response = await props.getImageStream(url.material_path)
return { url: url.material_path, response }
})
//按順序輸出。
for (const elementPromise of result) {
const temp = await elementPromise
zip.file(temp.url, temp.response)
}
const content = await zip.generateAsync({ type: 'blob' })
let temp = formData
temp[id] = content
setFormData(temp)
}
react清空state
這個其實算不上什麼難點和問題,當你在請求中使用setState時,由於請求是異步的,所以在你退出組件的時候可能請求還沒有發完,這時候就有可能還會調用setState,所以需要在退出組件的時候清空state,之前react用的是class的寫法,現在已經用react hook寫了,在react hook中清空的寫法與之前有些不同:
useEffect(() => {
if (!props.hideEdit) {
computerMaterialSize(dataSet.materials || [])
}
return () => {
setMaterialsSize = () => {
return
}
}
}, [])
這個寫法其實並不是很好,如果你有很多個setState的話,你就得一個個return,可能是現在用hook的人並不是很多,所以也沒有看到更好的寫法,如果有的話,還請指點一下。