0

I'm trying to return a value obtained in a promise statement and I'm getting continuously Promise { undefined }, someone knows how to do it correctly? When I render the .ejs file with the data it get's anything.

// /documents/vocabulary file
async function transformar() {
    return new Promise(resolve => {
        fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
            if (data) {
                var d = data.split('\n');
                var a = []
                for (let i=0; i<d.length; i++) {
                    a.push({
                        eng: d[i].split('-')[0].trim(),
                        esp: d[i].split('-')[1].trim()
                    })
                }

                a.sort(dynamicSort('eng'));
                resolve(a);
            }
        });
    });
}

//index.js file
const vocabulary = require('./documents/vocabulary');
const myFunction = async () => {
    const data = await vocabulary.transformar();
    return data;
}
const vocab = myFunction();
console.log(vocab)

app.use('/', function(req,res){
    res.render(path.join(__dirname+'/Vocabulary.ejs'), {vocabulary_data: vocab});
});

enter image description here

Thanks for reading!

arevilla009
  • 429
  • 3
  • 18
  • you need to await the promise. – Markus Dresch Dec 08 '20 at 12:45
  • 3
    What is `vocabulary` in `vocabulary.transformar()` ? – Anand Undavia Dec 08 '20 at 12:45
  • Since it is a promise you have to wait for it to resolve. I would replace your last line with `data.then(d => console.log('data', d))` – Keldan Chapman Dec 08 '20 at 12:46
  • 1
    `transformar` does not return a promise though. need to fix the function first. – Markus Dresch Dec 08 '20 at 12:47
  • 1
    There's 2 problems here. 1) the problem everyone has pointed out, that you have to `.then` on the `transformar` call, but 2) the fact that he's trying to `return a` in the `readFile` callback, but the promise won't resolve with `a` anyway, the way it's currently written – TKoL Dec 08 '20 at 12:48
  • 1
    There are 2 approaches to solve the second problem. 1) if you're using a recent version of node, `fs.promises.readFile` and all the other fs functions are available, or 2) make it into a promise yourself and `resolve(a)` instead of `return a.` – TKoL Dec 08 '20 at 12:49
  • 1
    Or the third approach, use the `fs-extra` library which deals with promises out of the box – TKoL Dec 08 '20 at 12:49
  • Does this answer your question? [How to read file with async/await properly?](https://stackoverflow.com/questions/46867517/how-to-read-file-with-async-await-properly) – Markus Dresch Dec 08 '20 at 12:50

3 Answers3

3

First of all, your transformar() method does not actually return a promise. Make sure you do that first:

function transformar() {
    return new Promise((resolve, reject) => {
        fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
            if (data) {
                var d = data.split('\n');
                var a = []
                for (let i=0; i<d.length; i++) {
                    a.push({
                        eng: d[i].split('-')[0].trim(),
                        esp: d[i].split('-')[1].trim()
                    })
                }

                a.sort(dynamicSort('eng'));
                resolve(a);
            } else {
                reject();
            }
        });
    });
}

Here are some suggestions:

  • You don't need to use async, since you already return a promise in the method now
  • All code paths should end up in reject() or resolve(), to avoid memory leaks (like what Thomas suggested in the comment below)

Now, back to the issue of getting data out of your promise: as there is no support for top-level await, so you need to either chain a .then() from the returned promise:

vocabulary.transformar().then((data) => console.log('data', data));

... or use await in an IIFE:

(async () => {
    const data = await vocabulary.transformar();
    console.log('data', data);
})();

If the Promise is being executed in a function, then you can simply use await:

const myFunction = async () => {
    const data = await vocabulary.transformar();
    console.log('data', data);
}

myFunction();
Terry
  • 63,248
  • 15
  • 96
  • 118
  • Thanks! Now my function returns a promise and sith myFunction() I can get data, however, it get's as Promise { } when I try to manipulate this data outside the function. const vocab = myFunction(); console.log(vocab) @Terry – arevilla009 Dec 08 '20 at 12:59
  • I modified a bit the main question @Terry – arevilla009 Dec 08 '20 at 13:03
  • `if(!data)` the promise will never resolve. This may create a memory leak. – Thomas Dec 08 '20 at 13:07
  • *"First of all, your transformar() method does not actually return a promise."* yes it does. An async function always returns a `Promise`. The same way that a function without a return statement always returns `undefined` – Thomas Dec 08 '20 at 13:11
  • @Thomas Thanks! I have updated my answer to ensure all code paths will either resolve or reject the promise. – Terry Dec 08 '20 at 14:59
1

You should try this code :

async transformar() {
return new Promise((resolve, reject) => {
  console.log('Sorting data');
  fs.readFile('src/documents/Vocabulary2.txt', 'utf8', async (error, data) => {
    if (data) {
      //format data on array-dict
      var d = data.split('\n');
      var a = []
      for (let i = 0; i < d.length; i++) {
        a.push({
          eng: d[i].split('-')[0].trim(),
          esp: d[i].split('-')[1].trim()
        })
      }
      //sort data
      await a.sort(dynamicSort('eng'));
      resolve(a);
    }
  });
})
}

var data;
vocabulary.transformar().then(result=>{
data = result;
console.log('data', data);
});
Mayur Kukadiya
  • 2,461
  • 1
  • 25
  • 58
  • It probably doesn't matter either way, but if you're returning a promise from `transformar` there's no requirement to label to function as `async`. Whether you do or not won't make a difference to the behavior of the function, I think, but I'm not sure if it's preferred to label them `async` or not – TKoL Dec 08 '20 at 12:51
  • when using `then` there's no reason for `async`. when using `await` inside the function, it has to be `async`. in this case, `await` is only used inside the callback, which is correctly defined as `async`, the outer function doesn't need it in this example. i wouldn't mix `async/await` and `then` though. – Markus Dresch Dec 08 '20 at 12:53
  • You are right, async in my answer can't effect anything, but anything outside of return promise, then that will run first and can't wait for return promise. – Mayur Kukadiya Dec 08 '20 at 12:53
1

Your asynchronous function isn't returning anything. Also, the fs.readFile function doesn't return promises, it takes a callback. You should wrap it within a promise and resolve it with your final value.

function transformar() {
  return new Promise(function (resolve, reject) {
    fs.readFile(
      'src/documents/Vocabulary2.txt',
      'utf8',
      function (error, data) {
        if (data) {
          //format data on array-dict
          var d = data.split('\n');
          var a = [];
          for (let i = 0; i < d.length; i++) {
            a.push({
              eng: d[i].split('-')[0].trim(),
              esp: d[i].split('-')[1].trim(),
            });
          }
          //sort data
          a.sort(dynamicSort('eng'));

          resolve(a);
        } else {
          reject(new Error('No data was found.'));
        }
      }
    );
  });
}

transformar()
  .then(function (data) {
    console.log(data);
  })
  .catch(function (err) {
    console.error(err);
  });

Sarah Dayan
  • 448
  • 3
  • 9