正确使用异步并等待

时间:2018-12-22 18:29:46

标签: javascript typescript asynchronous promise

下面的函数在for循环中调用几个异步函数。它解析不同的CSV文件以构建单个JavaScript对象。我想在for循环完成后返回对象。它在执行异步任务时立即返回空对象。是有道理的,但是我尝试了各种Promise / async / await组合,希望在for循环完成后再运行一些东西。我显然不明白发生了什么。对于这样的事情,是否有更好的模式可以遵循?还是我考虑不正确?

async function createFormConfig(files: string[]): Promise<object>

  return new Promise(resolve => {

    const retConfig: any = {};


    for (const file of files) {


      file.match(matchFilesForFormConfigMap.get('FIELD')) ?
        parseCsv(file).then(parsedData => {
          retConfig.fields = parsedData.data;
        })

        : file.match(matchFilesForFormConfigMap.get('FORM'))
        ? parseCsv(file).then(parsedData => retConfig.formProperties = parsedData.data[0])

        : file.match(matchFilesForFormConfigMap.get('PDF'))
          ? parseCsv(file).then(parsedData => retConfig.jsPdfProperties = parsedData.data[0])

          : file.match(matchFilesForFormConfigMap.get('META'))
            ? parseCsv(file).then(parsedData => {
              retConfig.name = parsedData.data[0].name;
              retConfig.imgType = parsedData.data[0].imgType;
              // console.log(retConfig);  <- THIS CONSOLE WILL OUTPUT RETCONFIG LOOKING LIKE I WANT IT
            })

            : file.match(matchFilesForFormConfigMap.get('PAGES'))
              ? parseCsv(file).then(parsedData => retConfig.pages = parsedData.data)
              : console.log('there is an extra file: ' + file);

    }

    resolve(retConfig);  // <- THIS RETURNS: {}
  });

这是我用来调用该函数的代码,希望使我的“ retConfig”充满CSV数据。

getFilesFromDirectory(`${clOptions.directory}/**/*.csv`)
  .then(async (files) => {
    const config = await createFormConfig(files);
    console.log(config);
  })
  .catch(err => console.error(err));

};

2 个答案:

答案 0 :(得分:2)

首先,一个async函数将返回一个Promise,因此您不必显式地返回一个。这是简化代码的方法:

async function createFormConfig(files: string[]): Promise<object> {

  // return new Promise(resolve => { <-- remove

  const retConfig: any = {};

  // ...

  // The value returned by an async function is the one you get
  // in the callback passed to the function `.then` 
  return retConfig;

  // }); <-- remove
}

然后,函数createFormConfig在完成计算之前返回配置。这是在返回之前计算它的方法:

async function createFormConfig(files: string[]): Promise<object> {

  const retConfig: any = {};

  // Return a Promise for each file that have to be parsed
  const parsingCsv = files.map(async file => {
    if (file.match(matchFilesForFormConfigMap.get('FIELD'))) {
      const { data } = await parseCsv(file);
      retConfig.fields = data;
    } else if (file.match(matchFilesForFormConfigMap.get('FORM'))) {
      const { data } = await parseCsv(file);
      retConfig.formProperties = data[0];
    } else if (file.match(matchFilesForFormConfigMap.get('PDF'))) {
      const { data } = await parseCsv(file);
      retConfig.jsPdfProperties = data[0];
    } else if (file.match(matchFilesForFormConfigMap.get('META'))) {
      const { data } = await parseCsv(file);
      retConfig.name = data[0].name;
      retConfig.imgType = data[0].imgType;
    } else if (file.match(matchFilesForFormConfigMap.get('PAGES'))) {
      const { data } = await parseCsv(file);
      retConfig.pages = data;
    } else {
      console.log('there is an extra file: ' + file);
    }
  });

  // Wait for the Promises to resolve
  await Promise.all(parsingCsv)

  return retConfig;
}

答案 1 :(得分:1)

异步函数已经返回了Promise,您无需将代码包装在新的代码中。只需从函数中返回一个值,调用者将收到一个承诺,该承诺将解析为返回的值。

此外,您已经实现了异步功能,但实际上并没有在任何地方使用await。因此,for循环会在您的任何promise解决之前贯穿整个循环。这就是为什么没有数据将其放入您的对象的原因。

仅使用await并摆脱then()调用将真正简化您的代码。例如,您可以执行以下操作:

async function createFormConfig(files: string[]): Promise<object> {

  const retConfig: any = {};

  for (const file of files) {

    if (file.match(matchFilesForFormConfigMap.get('FIELD')){
      // no need for the then here
      let parsedData = await parseCsv(file)
      retConfig.field = parsedData.data
    }

   // ...etc

最后,您可以返回值:

return retConfig