简体   繁体   中英

Race condition with async/await

I am having a problem (race condition) that I don't seem to be able to get past.

I added two comments to my console.log statements below. You can see the second gets called first and this is a problem because the var is empty then. I have async and await but still not sure why this is happening

router.put("/", async (req, res, next) => {
   var s3 = new AWS.S3();

  var params = {
    Bucket: "bucket1",
    Delimiter: '/',
    Prefix: req.body.params + '/'
  };

  var files= [];
  var tmp = '';

await s3.listObjectsV2(params, function(err, data) {

  for(var item in data.Contents){
      for(var anotherItem in data.Contents[item]){
        if(anotherItem == "Key"){
          tmp = data.Contents[item][anotherItem]
          var res = tmp.replace(req.body.params + '/', '')
          files.push(res)
        }
      }
   }
   this.allFiles = files
   console.log('allFiles - First', this.allFiles);  //this is logged second and has the correct data array

});

  console.log('allFiles - Second', this.allFiles);  //this gets logged first and is empty
  return res.send(this.allFiles);
});

You can only await a promise.

s3.listObjectsV2 appears to take a callback and not return a promise.

You need to wrap it in a promise.

await will only work with Promises. It looks like listObjectsV2 is callback-based, rather than Promise-based - you have to explicitly convert it to a Promise in order to await it, and you might as well have the Promise resolve to allFiles directly:

const allFiles = await new Promise((resolve, reject) => {
  s3.listObjectsV2(params, function(err, data) {
    if (err) reject (err);
    const files = [];
    for(var item in data.Contents){
      for(var anotherItem in data.Contents[item]){
        if(anotherItem == "Key"){
          tmp = data.Contents[item][anotherItem]
          var res = tmp.replace(req.body.params + '/', '')
          files.push(res)
        }
      }
    }
    console.log('allFiles - First', files);
    resolve(files);
  });
});
console.log('allFiles - Second', allFiles);
return res.send(allFiles);

The await keywords needs a promise to work with, but calling the function with a callback won't give you one. Instead, use the builtin method to get a promise :

router.put("/", async (req, res, next) => {
  var s3 = new AWS.S3();
  var params = {
    Bucket: "bucket1",
    Delimiter: '/',
    Prefix: req.body.params + '/'
  };
  var files = [];

  var data = await s3.listObjectsV2(params).promise();
  //  ^^^^^^^                             ^^^^^^^^^^^^
  for (var item in data.Contents) {
    for (var anotherItem in data.Contents[item]) {
      if (anotherItem == "Key") {
        var tmp = data.Contents[item][anotherItem]
        var res = tmp.replace(req.body.params + '/', '')
        files.push(res)
      }
    }
  }
  this.allFiles = files
  console.log('allFiles - First', this.allFiles);  //this is logged and has the correct data array
  return res.send(this.allFiles);
});

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