简体   繁体   中英

Javascript loop promises synchronously with additional parameter

I have an array of promises I try to process one after the others

function asyncInsertMagFromFile(file, resolve) {
            //some asynchronous processing with file
            resolve();
          }

let magRequests = fs.readdirSync(storeDir).map(function(file) {
            return new Promise(resolve => {
              asyncInsertMagFromFile(file, resolve);
            });
          });

I'm trying to process these promises (magRequests) "synchronously" without success. Lot's of questions about that subject exist but I havent found one that fit with my problem. I tried some of trincot solutions here https://stackoverflow.com/a/40329190/7531001 but I don't know how to handle my file parameter.

For example this doesn't work (await is only valid in async function)

(async function loop() {
            fs.readdirSync(storeDir).forEach(function(file) {
              await new Promise(resolve =>
                asyncInsertMagFromFile(file, resolve)
              );
            });

          })();

Any idea how I can fix this ?

You can try to use array.reduce to get a previous value (list of promises) and await it's completion before doing another call of the asyncInserMagFromFile function

(function loop() {
  fs.readdirSync(storeDir).reduce(async (previousPromise, file) => {
    await previousPromise;
    return new Promise(resolve =>
      asyncInsertMagFromFile(file, resolve);
    );
  },Promise.resolve());
})();

await is only valid in async function

Use .map instead of .forEach , and pass it an async function as a callback.

You can chain some 'then's one after another.

function asyncInsertMagFromFile(file) {
    return new Promise((resolve, reject) => {
        //some asynchronous processing with file
        // call resolve when finished
        resolve();
    })
}

let chain = Promise.resolve();

fs.readdirSync(storeDir).forEach(function (file) {
    chain = chain.then(((file) => () => asyncInsertMagFromFile(file))(file));
});

chain.then(() => {
    //finished
}).catch((err) => {
   //error in chain
})

Another way would be to use a for statement as mentioned in the comments:

function asyncInsertMagFromFile(file) {
    return new Promise((resolve, reject) => {
        //some asynchronous processing with file
        // call resolve when finished
        resolve();
    })
}

(async () => {
    const files = fs.readdirSync(storeDir);

    for (let i = 0; i < files.length; i++) {
        await asyncInsertMagFromFile(files[i]);
    }
})()

This may help you;

async function asyncInsertMagFromFile(file) {
 // use await to gather results from processing or return a promise
 return result;
}

function mapRequests(storeDir) {
  return fs.readdirSync(storeDir).map(async function(file) {
    return await asyncInsertMagFromFile(file);
  });
}

mapRequests(storeDir).then((mapped) => {});

The reason your example didn't work is because the forEach handler function uses await but is not declared async.

If you want to process in series and process the result then you could use recursion:

const FAILED = {};
const processImages = (files) => {
  const recur=(files,result) => {
    if(files.length===0){
      return result;
    }
    return asyncInsertMagFromFile(files[0])
    .catch(error=>FAILED)//always resolve, place FAILED values when rejected
    .then(success=>recur(files.slice(1),result.concat(success)))
  }
  return recur(files,[])
}

processImages(fs.readdirSync(storeDir)).then(results=>{
  console.log('failed',results.filter(r=>r===FAILED).length)
});

For parallel processing you can use Promise.all:

Promise.all(
  fs.readdirSync(storeDir).map((file)=> asyncInsertMagFromFile(file).catch(FAILED))
).then(results=>{
  console.log('failed',results.filter(r=>r===FAILED).length)
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM