Проблема потери точности 19-значного типа Number в JS pit

Node.js

источник

В последнее время при реализации требования необходимо получить доступ к стороннему интерфейсу, сначала вызвать интерфейс A, данные, возвращаемые интерфейсом A, содержат идентификатор задачи, а затем использовать идентификатор задачи для запроса интерфейса B для получения окончательного требуемого данные.

Серверная часть использует узел, поэтому пакет request-promise сначала используется для запроса стороннего интерфейса, однако после получения taskId, возвращаемого интерфейсом A, и вызова интерфейса 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, получаю новый идентификатор задачи, а затем использую новый идентификатор задачи для запроса интерфейса B, но результат нормальный!

Перепроверив код и убедившись, что запрашиваемые параметры в нормальном формате, я на какое-то время впал в бесконечную медитацию. . .

Находить

Сделав несколько попыток, я обнаружил, что последние две цифры идентификатора задачи, который я получаю с помощью запроса узла, равны 0, т.е.1152921504735848700, и taskId, полученный почтальоном, относительно нормальный.1152921504735848759, затем я делаю следующее в консоли узла

В это мгновение я понял. TaskId в интерфейсе A представляет собой 19-значное число, и когда запрос-обещание разбирает данные в json, 19-значное число теряет свою точность.После проверки данных обнаруживается, что числовой тип js имеет максимум безопасное значение.То есть 2 в 53-й степени (9007199254740992), за пределами этого значения возникнет проблема потери точности. Орз

Получите правильные данные ответа

Поскольку в начале использовался пакет request-promise, полученный идентификатор задачи потерял свою точность, поэтому вместо этого для отправки запроса использовался собственный 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() вызовет проблему потери точности, что очень смущает Орза.

Если этот интерфейс уже управляемый, то 19-значное число можно преобразовать в строку, чтобы не было ошибки при парсинге, но поскольку это сторонний интерфейс, его нельзя изменить. Тогда самое быстрое решение - изменить запрос языка программирования ╮(╯_╰)╭

окончательное решение

В конце концов, я все же использовал node, но использовал более жесткое решение: сначала найти 19-значное число в полученной строке JSON, затем добавить к нему кавычки, а затем использовать 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 некоторое время, потому что он не требует вычислений больших чисел, поэтому все числа и идентификаторы хранятся в виде строк, и я никогда не сталкивался с этой проблемой. В этот раз я действительно столкнулся с этим, должен сказать, что js действительно немного слабоват в этом отношении, после этого я также пробовал использовать Go и python для запросов, и все они были правильно обработаны. ┑( ̄Д  ̄)┍ Но узел по-прежнему очень удобен в использовании, ╮(╯▽╰)╭

Thanks!