JS大坑之19位数的Number型精度丢失问题
栏目: JavaScript · 发布时间: 5年前
内容简介:最近在实现一个需求的时候,需要接入第三方的接口,先调用A接口,A接口返回的数据里,有一个taskId,然后再使用这个taskId请求B接口,获取最终需要的数据。后端使用的是node,因此最开始使用的是request-promise这个包请求第三方接口,然而在获取A接口返回的taskId之后,调用B接口之后,B接口的响应居然是系统错误!简易代码如下接着我使用postman请求A接口,获取新的taskId,再用新的taskId请求B接口,结果却是正常的!
最近在实现一个需求的时候,需要接入第三方的接口,先调用A接口,A接口返回的数据里,有一个taskId,然后再使用这个taskId请求B接口,获取最终需要的数据。
后端使用的是node,因此最开始使用的是request-promise这个包请求第三方接口,然而在获取A接口返回的taskId之后,调用B接口之后,B接口的响应居然是系统错误!简易代码如下
const rp = require('request-promise') const { taskId } = await rp('https://xxx.com/A') const options = { method: 'POST', uri: 'https://xxx.com/B', body: { taskId }, json: true } const result = await rp(options) // { // "errorcode": "40001", // "message": "系统错误", // "status": "failed" // } 复制代码
接着我使用postman请求A接口,获取新的taskId,再用新的taskId请求B接口,结果却是正常的!
我在反复检查代码,确认请求的参数都是正常的格式之后,一时陷入了无尽的沉思之中。。。
发现
在做了几次尝试之后,我发现使用node请求得到的taskId最后两位数都是0,即 1152921504735848700 ,而使用postman获取的taskId,则是比较正常的是 1152921504735848759 ,接着我在node控制台做如下操作
就是这么一瞬间,顿悟了。A接口里的taskId是个19位数字,而request-promise在将数据解析成json时,导致这个19位的数字丢失了精度,查了下资料,发现js的number类型有个最大安全值, 即2的53次方(9007199254740992),超过这个值就会出现精度丢失的问题。 Orz
获取正确的响应数据
由于在一开始使用request-promise包,因此获取的taskId是丢失了精度了,因此改用了node原生的http模块发送请求。
const req = https.request('https://xxx.com/A', (res) => { res.on('data', (chunk) => { // 由于这里获取到的响应数据是JSON字符串,因此19位的数字只是字符串的一部分,这时获取到的taskId就是正确的数字 console.log(`BODY: ${chunk}`); }); res.on('end', () => { console.log('No more data in response.'); }) }) 复制代码
虽然获取到了正常的响应数据,但是这是个JSON字符串,接下来还要把这个字符串解析成JSON,但是用JSON.parse(),又会引起精度丢失的问题,这可真尴尬 Orz
如果这个接口是已方可控的,那么就可以把这个19位数的number转成字符串,这样在解析的时候就不会出错了,但是由于是第三方接口,因此没法改变。那么最快的解决方案,就是换种编程语言请求啊╮(╯_╰)╭
最后的解决
好吧,最后还是用了node,不过我用了比较硬核的方案实现,先在获取的JSON字符串中,找到这个19位的数字,然后为它加上引号,这样再用JSON.parse()解析的时候,就能保持正常的数值,这样接下的流程就自然通了,代码如下
let result = '{"taskId":1152921504735848759,"status":"CREATED","progress":0.0,"success":true}' // JSON.parse(result) 不为19位数补上双引号,直接parse时,精度丢失,结果如下: // { // taskId: 1152921504735848700, // status: 'CREATED', // progress: 0, // success: true // } const taskId = result.match(/[0-9]{19}/)[0] // 正则获取19位数字的值 result = result.replace(taskId,`"${taskId}"`) // 补上双引号 const data = JSON.parse(result) // { // taskId: '1152921504735848759', // 解析出来之后是字符串,因此没有丢失精度 // status: 'CREATED', // progress: 0, // success: true // } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 我靠!Semaphore里面居然有这么一个大坑!
- 漫谈分布式系统(零):挖他个大坑
- 10 个内存引发的大坑,你能躲开几个?(一)
- 10 个内存引发的大坑,你能躲开几个?(二)
- 10 个内存引发的大坑,你能躲开几个?(三)
- c – 这个基地有多少位数?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。