0

I'm a rookie in nodejs.And now I occured a problem about Asynchronous and Synchronous in node js.

Here is my code:

var time_begin = Date.now();
console.log("begin time:" + time_begin);

arr_a = ['a', 'b', 'a', 'b', 'a', 'b']

async function iterTrans (arr_a) {

    var txs_arr_tmp = [];
    for(let aInfo of arr_a) {
        var fs = require('fs');
        if (aInfo == "a") {
            fs.readFile("./debug.json", function (error_file, data_file) {
                if (error_file) {
                    console.log(error_file)
                } else {
                    txs_arr_tmp.push("a");
                }
            });

        } else {
            txs_arr_tmp.push("b");
        }
    }
    return txs_arr_tmp;
}

iterTrans(arr_a).then((txs_arr_tmp) => {

    var content_str = JSON.stringify(txs_arr_tmp);
    console.log(content_str);
})

I hode that the console will print:

["a","b","a","b","a","b"]

But I actually got:

["b","b","b"]

I've learned and tried some methods about Asynchronous to Synchronous, but I didn't succeed.And I must use Asynchronous method in readFile.

So how can I get ["a","b","a","b","a","b"]? Who can give me some advice?

Thanks a lot!

  • It looks like you're trying to get `['a', 'b', 'a', 'b', 'a', 'b']`, not `["a","b","a","a","a","a"]`. Can you explain why you're expecting the `b` to be replaced with `a`? – Patrick Roberts Apr 04 '20 at 16:15
  • can you post a working code snippet? – SpiritOfDragon Apr 04 '20 at 16:20
  • 1
    The readFile in you async function is still asynchronous. Use a synchronous file read in order to make sure txs_arr_tmp is done being constructed before returning. See this post on how to read multiple files: https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop/37576787#37576787 – Ignacy Debicki Apr 04 '20 at 16:23
  • @IgnacyDebicki that answer does not have a "synchronous" file read. It's still asynchronous, it's just promise-based instead of callback-based. – Patrick Roberts Apr 04 '20 at 16:26
  • @PatrickRoberts I know. And that is causing the issue. The for loop cycles over the asynchronous file reads, puts in the b's into the array, and returns the array before they have a chance to complete their callback pushing the appropriate values into the army. Therefore, he needs to use synchronous file reads inside the for loop, so that the asynchronous function only returns once all the files have been read – Ignacy Debicki Apr 04 '20 at 16:33
  • @PatrickRoberts thank you!that's my mistake, and i really expect ["a","b","a","b","a","b"] – Vincent Wang Apr 04 '20 at 16:36
  • "*How to convert Asynchronous to Synchronous*" - you don't. Instead, you make the rest of the code deal with the asynchrony. – Bergi Apr 04 '20 at 16:40
  • @IgnacyDebicki thank you for your participate. I'm sorry that I must use Asynchronous method in readFile in my project. and my concern is that how to Execute code in sequence with Asynchronous method in for loop – Vincent Wang Apr 04 '20 at 16:43
  • @Bergi thank you for participate, so how can i get ["a","b","a","b","a","b"]?thanks a lot! – Vincent Wang Apr 04 '20 at 16:45
  • @VincentWang [Make a promise for the file contents](https://stackoverflow.com/a/30302477/1048572) so that you can `await` it. – Bergi Apr 04 '20 at 16:48
  • @SpiritOfDragon,the code on my question is working, But it didn't turn out to be what I wanted ,how can I get ["a","b","a","b","a","b"] ? thanks a lot! – Vincent Wang Apr 04 '20 at 16:49

2 Answers2

1

You cannot convert something async into sync, but you can make your rest implementation wait for the async func to complete

var time_begin = Date.now();
console.log("begin time:" + time_begin);
arr_a = ['a', 'b', 'a', 'b', 'a', 'b']

async function iterTrans(arr_a) {
  var txs_arr_tmp = [];
  for (let aInfo of arr_a) {
    const fs = require('fs').promises;
    if (aInfo == "a") {
      try {
        await fs.readFile("./debug.json")
        txs_arr_tmp.push("a");
      } catch (error) {
        console.log(error)
        var obj_addr = {
          "success": false,
          "error_no": 1,
          "error_info": "err with addrs"
        }
        return res_send.jsonp(obj_addr);
      }
    } else {
      txs_arr_tmp.push("b");
    }
  }
  return txs_arr_tmp;
}

iterTrans(arr_a).then((txs_arr_tmp) => {
  var content_str = JSON.stringify(txs_arr_tmp);
  console.log(content_str);
}) 

If fs' promisified methods are not available

var time_begin = Date.now();
console.log("begin time:" + time_begin);
arr_a = ['a', 'b', 'a', 'b', 'a', 'b']

async function iterTrans(arr_a) {
  var txs_arr_tmp = [];
  for (let aInfo of arr_a) {
    let util = require('util');
    const readFile = util.promisify(require('fs').readFile);
    if (aInfo == "a") {
      try {
        await readFile("./debug.json")
        txs_arr_tmp.push("a");
      } catch (error) {
        console.log(error)
        var obj_addr = {
          "success": false,
          "error_no": 1,
          "error_info": "err with addrs"
        }
        // return res_send.jsonp(obj_addr);
      }
    } else {
      txs_arr_tmp.push("b");
    }
  }
  return txs_arr_tmp;
}

iterTrans(arr_a).then((txs_arr_tmp) => {
  var content_str = JSON.stringify(txs_arr_tmp);
  console.log(content_str);
})
AZ_
  • 3,094
  • 1
  • 9
  • 19
  • Thank you for your answer. But I got that error : TypeError: Cannot read property 'readFile' of undefined – Vincent Wang Apr 04 '20 at 17:04
  • @VincentWang you must be using an old version of Node.js. [The `fs.promises` API was added in version 10](https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fspromises_readfile_path_options) – Patrick Roberts Apr 04 '20 at 17:05
  • @VincentWang then you can use `let util = require('util'); const readFile = util.promisify(require('fs').readFile);` – AZ_ Apr 04 '20 at 17:11
1

Here's how I would approach writing the function. Since your version of Node.js doesn't have support for the fs.promises API, you can use util.promisify() to convert fs.readFile() from a callback API to a promise API, then use Array.prototype.map() to create an array of promises and await the readFile() calls in parallel with Promise.all():

const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);

const time_begin = Date.now();
console.log("begin time:" + time_begin);

const arr_a = ['a', 'b', 'a', 'b', 'a', 'b'];

async function iterTrans (arr_a) {
  // array map allows parallel asynchronicity
  const txs_arr_tmp_promises = arr_a.map(async aInfo => {
    // use early return (to avoid nesting large blocks inside if statements)
    if (aInfo !== 'a') return 'b';

    // let await throw here if file error occurs
    const data_file = await readFile('./debug.json');
    return 'a';
  });

  return Promise.all(txs_arr_tmp_promises);
}

iterTrans(arr_a).then(txs_arr_tmp => {
  const content_str = JSON.stringify(txs_arr_tmp);
  console.log(content_str);
}).catch(error => {
  // handle errors here
  console.log(error);
});
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153