JS大坑之19位数的Number型精度丢失问题详解


Posted in Javascript onApril 22, 2019

More

本项目仅供爬取体验,每次访问都会实时爬取数据,所以数据返回速度会比较慢,实际操作应该是定时爬取数据然后将数据存进数据库,数据从数据库返回从而提高数据返回效率。

但项目很基础,可以作为以上各个node模块最基础的练手使用,希望可以帮到大家 ?起源

最近在实现一个需求的时候,需要接入第三方的接口,先调用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控制台做如下操作

JS大坑之19位数的Number型精度丢失问题详解

就是这么一瞬间,顿悟了。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 
// }

结语

使用node也有一段时间了,因为涉及不到大数计算,因此对于编号啊,ID啊,都是用字符串形式进行存储的,也就一直没有遇到这个问题。这一次居然碰上了,不得不说js在这一方面确实有点弱势,之后也尝试了下使用Go,python进行请求,都是能正确解析不过node使用起来还是很舒服的

以上所述是小编给大家介绍的JS大坑之19位数的Number型精度丢失问题详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
替代window.event.srcElement效果的可兼容性的函数
Dec 18 Javascript
原生JS操作网页给p元素添加onclick事件及表格隔行变色
Dec 01 Javascript
javascritp添加url参数将参数加入到url中
Sep 25 Javascript
JavaScript中window.showModalDialog()用法详解
Dec 18 Javascript
jquery获取checkbox的值并post提交
Jan 14 Javascript
JS实现具备延时功能的滑动门菜单效果
Sep 17 Javascript
jQuery点击输入框显示验证码图片
May 19 Javascript
用js实现简单算法的实例代码
Sep 24 Javascript
Easyui ueditor 整合解决不能编辑的问题(推荐)
Jun 25 Javascript
vue.js如何更改默认端口号8080为指定端口的方法
Jul 14 Javascript
vue使用vue-cli快速创建工程
Jul 28 Javascript
如何在selenium中使用js实现定位
Aug 18 Javascript
Vue $mount实战之实现消息弹窗组件
Apr 22 #Javascript
深入理解vue中的slot与slot-scope
Apr 22 #Javascript
浅析vue插槽和作用域插槽的理解
Apr 22 #Javascript
详解50行代码,Node爬虫练手项目
Apr 22 #Javascript
Vue匿名插槽与作用域插槽的合并和覆盖行为
Apr 22 #Javascript
详解Vue 匿名、具名和作用域插槽的使用方法
Apr 22 #Javascript
详解Node.js一行命令上传本地文件到服务器
Apr 22 #Javascript
You might like
html中select语句读取mysql表中内容
2006/10/09 PHP
php xml-rpc远程调用
2008/12/19 PHP
FirePHP 推荐一款PHP调试工具
2011/04/23 PHP
php数组函数序列之asort() - 对数组的元素值进行升序排序,保持索引关系
2011/11/02 PHP
php微信开发自定义菜单
2016/08/27 PHP
js中将字符串转换成json的三种方式
2011/01/12 Javascript
浅析javascript中function 的 length 属性
2014/05/27 Javascript
JavaScript类继承及实例化的方法
2015/07/25 Javascript
jQuery实现鼠标双击Table单元格变成文本框及输入内容后更新到数据库的方法
2015/11/25 Javascript
JS制作图形验证码实现代码
2020/10/19 Javascript
JS针对Array的各种操作汇总
2016/11/29 Javascript
js实现瀑布流效果(自动生成新的内容)
2017/03/16 Javascript
微信小程序新增的拖动组件movable-view使用教程
2017/05/20 Javascript
vue.js内部自定义指令与全局自定义指令的实现详解(利用directive)
2017/07/11 Javascript
vue的全局提示框组件实例代码
2018/02/26 Javascript
vue移动端微信授权登录插件封装的实例
2018/08/28 Javascript
使用jquery的cookie实现登录页记住用户名和密码的方法
2019/03/13 jQuery
[01:39](回顾)各路豪强针锋相对,几经鏖战四强产生
2014/07/01 DOTA
[05:23]DOTA2-DPC中国联赛2月1日Recap集锦
2021/03/11 DOTA
Python使用Redis实现作业调度系统(超简单)
2016/03/22 Python
Python正则表达式常用函数总结
2017/06/24 Python
Django缓存系统实现过程解析
2019/08/02 Python
Python使用贪婪算法解决问题
2019/10/22 Python
opencv设置采集视频分辨率方式
2019/12/10 Python
详解如何使用Pytest进行自动化测试
2021/01/14 Python
python 多线程爬取壁纸网站的示例
2021/02/20 Python
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
StubHub意大利:购买和出售全球演唱会和体育赛事门票
2017/11/21 全球购物
Space NK英国站:英国热门美妆网站
2017/12/11 全球购物
BookOutlet加拿大:在网上书店购买廉价折扣图书和小说
2018/10/05 全球购物
新西兰最大的连锁超市:Countdown
2020/06/04 全球购物
自我评价个人范文
2013/12/16 职场文书
网络管理专业求职信
2014/03/15 职场文书
公务员保密承诺书
2014/03/27 职场文书
主持人演讲稿
2014/05/13 职场文书
大学升旗仪式主持词
2015/07/04 职场文书