采坑记录-接口联调中的精确度丢失

上下文

先简单描述一下业务场景吧:在页面 A 里调用接口 1 新建工单,首先接口 1 是走我们前端自己的 Node 后台进行一层处理后,再调后台同学的接口。之后后台同学会返回这个新工单的 ID,再由 Node 后台把这个 ID 照样返回给浏览器。页面 B 是一个工单列表,调用的是后台同学的接口 2 拉取所有的工单信息。在本次迭代的需求中新增了一些 feature 后,我和后台同学联调这里的业务逻辑是否符合预期时,结果发现我在页面 A 新建工单后得到的工单 ID,和在页面 B 的工单列表里看到的工单 ID 是不一样的。这里的工单 ID 是指类似于 1288407554284482560 这样的一串数字。

排查问题

在清楚问题是什么后,我首先确认下前端代码,看看是不是在拿到后台接口数据后对数据有进行修改等操作,结果很遗憾并没有这么轻松地定位到问题。接着我就去找对接的后台同学一起排查问题,首先确认接口是否的确走的是测试环境的数据,以及后台代码是否有对返回的数据进行额外的处理等。

都没发现问题后,使用 curl 直接调后台同学创建工单的接口,得到的工单 ID 和先走 Node 再走后台同学接口拿到的工单 ID 的确不一致。于是大致可以确定问题是出在了我们这边的 Node 后台代码上。之后我自己再验证了下,上机器看 Node 后台的日志,发现 Node 最初拿到后台接口的数据和最后返回给浏览器的数据确实是不一样的。

于是我一行行去看 Node 的代码,试图寻找是在哪个地方改变了数据。几轮看下来后,我还是没有找到修改数据的地方所在,只能挨块打印出当前的数据,然后上机器看日志。最后发现是在 JSON.parse(data) 这句代码执行完后,工单 ID 就发生了改变的(这里的 data 是指后台同学接口返回的数据)。这里使用 JSON.parse 是因为后台接口返回的 data 是字符串形式的,所以先使用 JOSN.parse 解析一下。那我就纳闷了,JOSN.parse 不就是把字符串解析成对象而已吗,怎么前后的数据还会改变的?

问题原因

通过仔细研究 data 里的数据后(比如 data = "{\"work_id\":1288407554284482560}"),我发现 data 里的 work_id 是 Number 类型的!这里暂且不讨论前后端接口传输的 ID 为啥是 Number 类型而不是 String 类型,其实我也不知道当初定这接口的人是怎么想的。又联想到前后 work_id 的值其实相差很小,而且这个 work_id 高达 19 位数,所以就怀疑会不会是发生了精确度丢失?

const data = "{\"work_id\":1288407554284482560}"
const res = JSON.parse(data);
console.log(typeof res.work_id);  // number
console.log(res);  // {work_id: 1288407554284482600}

以前也有了解过遵循 IEEE 754 规范的计算机语言,由于十进制小数转化为二进制时成了一个无限循环小数,导致进行四则运算后的结果并不准确,最被人知晓的就是 0.1 + 0.2 !== 0.3 了,很久之前我还为此写了一篇 Blog 记录呢。

猜到可能的原因后,我就尝试着把原先 data 里的 work_id 转换成字符串类型再 JOSN.parse 试试看,结果发现这样得到的 work_id 就准确了!所以这次 bug 就确认了是 JOSN.parse 时,由于 work_id 的值超出了 JS 能表示的最大整数值即 Number.MAX_SAFE_INTEGER = 9007199254740991,所以发生了精确度溢出,最后解析得到的 work_id 也就不准确了。

const data = "{\"work_id\":1288407554284482560}"
const formatdata = data.replace(/(\d+)/g, "\"$1\"");
const res = JSON.parse(formatdata);
console.log(res);  // {work_id: "1288407554284482560"}

参考资料

当JavaScript遇上UINT64


 上一篇
腾讯实习小结 腾讯实习小结
又到了一段实习经历暂告结束的时候啦~ 时间说快吧,这两个多月的时间里也遇到了几多困难和挑战,在其中也挣扎许久。时间说慢吧,现在也已经快要离职并开始秋招这个最后的难关了。姑且就以此篇 Blog 作为我在腾讯从 2020.06.10 实习以来
2020-08-14
下一篇 
前端性能监控指标介绍 前端性能监控指标介绍
为什么要统计监控指标量化出一个页面的性能,根据不断阶段的耗时,找出其中的短板进行性能优化。 监控指标数据window.performance APIW3C MDN Performance performance.memory 表示内存
2020-07-18
  目录