【问题标题】:Why does my promise chain not run sequentially when I use an exported module, but it does when I don't export the module?为什么当我使用导出的模块时我的承诺链没有按顺序运行,但是当我不导出模块时它会运行?
【发布时间】:2020-06-02 15:43:44
【问题描述】:

我正在尝试从一个文件中的 URL 获取数据作为 csv,然后使用承诺链将另一个 javascript 文件中的该 csv 转换为 JSON 对象。问题是,它试图在回调实际检索到 csv 之前将 csv 转换为 json。
(注意:我意识到我正在使用现在已弃用的请求库,一旦我有承诺链工作就会更改它)。

文件 1(从 url 获取 csv 数据)如下。我需要轮询 url,直到它被数据填充(通常需要大约 5 分钟 - 事先有身份验证步骤,但它们工作正常,所以我省略了它们以简化这一点。)这将返回一个承诺,它被导出为 get_csv ()。承诺解析为 csv 字符串。

const request = require('request');

const csv_url = foo;

module.exports.get_csv = () => {
  return new Promise ((resolve, reject) => {
    function poll_url () {
      request.get({url: csv_url
      }, (error, response, body) => {
        if (error) console.error(error);
        data_csv = body;
        if (data_csv === ''){                             
          console.log("data_csv is blank.");
        } else {
          clearInterval(intervalID);
          console.log("data_csv has been populated!");
          resolve(data_csv);
        }
      })                        
    }                           

    pollURL()
    var intervalID = setInterval(poll_url, 60000);
  })                            
};

JS 文件 2(将 csv 数据更改为 json)如下。这意味着只需将 .then() 添加到文件 1 中的 Promise 中,这会将数据从 csv 转换为 json,然后将 console.log() 转换为 json 数据。

const data = require('./get_csv_data');   // File 1
const csv = require('csvtojson');

new Promise ((resolve, reject) => {
  const data_csv_string = data.get_csv()
  resolve(data_csv_string);
})

.then(
  (data_csv_string) => {
    console.log(data_csv_string);                        // for debugging - giving undefined
    csv({output: "json"}).fromString(data_csv_string)    // "Cannot read property 'toString' of undefined"
    .then((data_json_string) => {
      console.log(data_json_string);
    })
  }
)

问题是文件 2 的 .then() 没有等待第一个 Promise 的回调完成。控制台打印出来:

undefined
(node:10691) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'toString' of undefined
data_csv is blank.
data_csv is blank.
data_csv is blank.
data_csv is blank.
data_csv is blank.
data_csv is blank.
data_csv has been populated!

我无法理解的是,如果我将 .then() 子句放在文件 1 的末尾,它确实有效。我做错了什么?

.then(
  (data_csv_string) => {
    console.log(data_csv_string);                        
    csv({output: "json"}).fromString(data_csv_string)    
    .then((data_json_string) => {
      console.log(data_json_string);
    })
  }
)

【问题讨论】:

  • 你没有拒绝错误,如果字符串为空,你也没有解析。
  • 如果您现在必须使用请求,请使用request-promise。现在你正在用头撞墙,因为你在强迫自己从头开始实现一个承诺。使用返回承诺的库来简化问题。

标签: javascript node.js promise callback


【解决方案1】:

文件 2 需要:

const data = require('./get_csv_data');   // File 1
const csv = require('csvtojson');

data.get_csv().then(
  (data_csv_string) => {
    console.log(data_csv_string);                        // for debugging - giving undefined
    csv({output: "json"}).fromString(data_csv_string)    // "Cannot read property 'toString' of undefined"
    .then((data_json_string) => {
      console.log(data_json_string);
    })
  }
)

【讨论】:

    【解决方案2】:

    这个区块

    new Promise ((resolve, reject) => {
      const data_csv_string = data.get_csv()
      resolve(data_csv_string);
    })
    .then(
    

    错了。如果你想在一个 Promise 中包装一些东西(可能是一个 Promise,也可能是一个值),这样做

    Promise.resolve(data.get_csv())
        .then(//...
    

    但是,正如您所知,data.get_csv() 已经返回了一个承诺,因此没有必要这样做。只需将您的 Promise 构造函数替换为

    data.get_csv()
        .then(//...
    

    顺便说一句,有一个 Node 实用程序用于承诺基于回调的函数:util.promisify

    【讨论】:

    • 啊,好点,我在承诺中返回了一个承诺,尽管一旦我将它更改为您的建议,唯一打印到控制台的内容是:`TypeError: Cannot read property 'then' of undefined`暗示它仍然尝试在 data.get_csv() 完成之前执行 .then()
    【解决方案3】:

    setInterval 应该避免用于这样的用例,因为在某些情况下,可以将回调的多次调用加入队列,然后“快速”退出队列。当您知道自己准备好时,最好显式调用重试尝试;这给了你更多的控制权。

    您的代码存在许多问题。

    例如:

    pollURL()
    var intervalID = setInterval(poll_url, 60000)
    

    这些行启动轮询,即使第一次尝试成功,这可能不是您想要的。

    这是一个重构(显然未经测试):

    const request = require('request')
    
    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
    
    const repeatUntil = ({fn, condition, interval}) => {
        const go = async (...args) => {
            const result = await fn(...args)
            if(condition(result)) return result
            else {
                await delay(interval)
                await go(...args)
            }
        }
        return go
    }
    
    const getURL = (url) => new Promise((resolve, reject) => 
        request.get({url}, (error, response, body) => {
            if (error) return reject(error)
            resolve({response, body})
        }))  
    
    const csv_url = 'http://example.com'
    const condition = ({body}) => !!body
    const interval = 60000
    const getCSV = repeatUntil({ fn: () => getURL(csv_url), condition, interval })
    
    module.exports.getCSV = getCSV
    

    文件 1

    const getCSV = require('./get-csv')   // File 1
    const csvToJSON = require('csvtojson')
    
    getCSV()
        .then(({body}) => {
            const json = csvToJSON({output: "json"}).fromString(body) 
            console.log(json)
        })
    
    

    【讨论】:

      猜你喜欢
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-06
      • 2012-03-09
      • 2019-06-26
      • 1970-01-01
      相关资源
      最近更新 更多