异步等待JSON解析未等待

时间:2019-01-26 18:14:13

标签: arrays node.js json async-await

我遇到async/await不等待上一条语句的问题。我正在尝试读取JSON文件,然后尝试操纵结果对象。 尽管有await,但在执行下一个功能时,该数组仍未定义。

function initJson(file) {
  let obj;
  fs.readFile(file, 'utf8', (err, data) => {
  if (err) {
    throw err;
  }
  try {
    const obj = JSON.parse(data);
    console.log(obj);
   }
   catch (err) {
     throw err;
   }
 });
 return obj;
};

filterItemForKeyValue = function filterItemForKeyValue(obj, k, v) {
  if (typeof obj !== 'undefined' && obj) {
    return obj.filter(item => item[k] !== v);
  }
  return false;
};

async function handleFavorites(file) {
  let favorites = await initJson(file);
  favorites = await filterItemForKeyValue(favorites, 'delete', true);
  // here it's already false because favorites is undefined because it didn't wait

  console.log(favorites);
  return favorites;
};

let favs = handleFavorites('test.json');
console.log(favs);

2 个答案:

答案 0 :(得分:4)

您的initJson没有使用Promise,因此您无法等待响应。 通过返回如下所示的承诺来修订initJson

function initJson(file) {
    return new Promise((resolve, reject) => {
        fs.readFile(file, 'utf8', (error, data) => {
            if (error) {
                reject(error);
            }
            resolve(JSON.parse(data));
        });
    });
};

还为您转换了filterItemForKeyValue

var filterItemForKeyValue = function filterItemForKeyValue(object, key, value) {
    return new Promise((resolve, reject) => {
        try {
            if (typeof object !== 'undefined' && object) {
                resolve(object.filter(item => item[key] !== value));
            }
        } catch (error) {
            reject(false);
        }
    });
};

这是您在答案中提供的更改之后与结果进行交互的方式。

handleFavorites('test.json').then((result) => {
  console.log(result);
});

承诺:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

异步功能:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

答案 1 :(得分:2)

您的函数initJson不会将其结果作为承诺返回,因此您无法await

该函数的核心是对fs.readFile()的调用,该调用本身不会返回promise。您可以使用util.promisify进行更改,而initJson函数可以将其作为承诺返回。

在您的过滤器函数上:它是在调用堆栈上同步发生的,并且不涉及异步操作,因此不需要不需要包装在异步中或返回promise。也没有理由将其分配给变量,尤其是没有全局变量。只需使用函数声明,这就是您所需要的。

请注意,最后的doIt函数将对异步操作的调用包装在async关键字中。您也可以这样做,就像@Win的答案一样。

最后,我修改了答案,以显示您将在呼叫中处理错误的位置。根据您的需要,您可能希望重新抛出该错误并在等待最终结果的地方捕获该错误,这取决于您。

PS:我强烈建议您检出this video on call stack vs event loop in JavaScript。这很有趣,可以帮助您更好地理解为什么有些电话不同步的原因。

const {promisify} = require('util')
const fs = require('fs')
const readFileAsync = promisify(fs.readFile)

async function initJson(file) {
  const data = await readFileAsync(file, 'utf8')
  return JSON.parse(data)
}

function filterItemForKeyValue(object, key, value) {
  if (typeof object !== 'undefined' && object) {
    return object.filter(item => item[key] !== value)
  }
}

async function handleFavorites(file) {
  try {
    let favorites = await initJson(file)
    return await filterItemForKeyValue(favorites, 'delete', true)
  } catch (err) {
    console.error('error', err.message)
  }
}

async function doIt(){
  const favorites = await handleFavorites('test.json')
  console.log(favorites)
}

doIt()