Skip to content
Advertisement

Returning Value From Callback in a Map

My problem is depending on the result of an asynchronous operation during a map. I know similar questions have been asked, but cannot quite figure out how to apply this to my case.

I have figured out how to solve my problem using synchronous methods, my code is as follows,

    if (store.store !== {}) {
      const files = Object.values(store.store).map((file, index) => {
        return fs.existsSync(file.fileName) ? file : FILES[index]; 
      });
    }

I want to try writing this using asynchronous callbacks. I am new to javascript and just want to get the hang of these callbacks, this is what I tried, but it obviously does not work.

if (store.store !== {}) {
  const exists = (path: string) => {
    return fs.access(path, fs.constants.F_OK, (e) => {
      return e ? false : true;
    });
  };
  const files = Object.values(store.store).map((file, index) => {
    return exists(file.fileName)? file : FILES[index];
  });

I assume during execution the code does not wait for the result of the call back. However, using async/await with fs.promises.access does not work because I think the map function doesn’t work asynchronously. Anyways, I want to be able to do this without async/await, is there any way to do this?

Advertisement

Answer

UPDATED SOLUTION: Easy way: wrap the below solution in a Promise:

const getAuthorizedFiles => storeObj => new Promise((resolve, reject) => {
  const doneCount = Object.keys(storeObj).length;

  if (doneCount === 0) {
    reject();
  } else {
    const files = [];
    let count = 0;

    Object.values(storeObj).forEach((file, index) => {
      fs.access(file.fileName, fs.constants.F_OK, err => {
        files[index] = !err ? file : null;
        count += 1;
        if (count === doneCount) {
          resolve(files);
        }
      });
    });
  }
});

// Implementation:
getAuthorizedFiles(store.store).then(files => { console.log(files[0]); });

If OP just really doesn’t want to deal with Promises for some reason, (though they are now a part of the spec) then they can also just make their own fun callback pattern function:

const getAuthorizedFiles = (storeObj, cb) => {
  if (typeof cb !== 'function') { cb = x => x; }
  
  const doneCount = Object.keys(storeObj).length;

  if (doneCount === 0) {
    cb(new Error(`I don't want to deal with an empty object`));
  } else {
    const files = [];
    let count = 0;

    Object.values(storeObj).forEach((file, index) => {
      fs.access(file.fileName, fs.constants.F_OK, err => {
        files[index] = !err ? file : null;
        count += 1;
        if (count === doneCount) {
          cb(null, files);
        }
      });
    });
  }
};

// Implementation:
getAuthorizedFiles(store.store, files => { console.log(files[0]); });

ORIGINAL SOLUTION:

This is a simple way to do it without async/await. Set the files value as an empty array. Swap out the .map for a .forEach so you are no longer concerned with trying to return some value from an asynchronous function. Sounds like order is important, so when the access callback resolves, assign the file from the store.store Array into the new files Array.

if (store.store !== {}) {
  const files = [];

  Object.values(store.store).forEach((file, index) => {
    fs.access(file.fileName, fs.constants.F_OK, err => {
      files[index] = !err ? file : null;
    });
  });
}
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement