Проблема обратного вызова AWS S3 / Javascript - PullRequest
0 голосов
/ 16 января 2019

Итак, у меня проблема с асинхронным выполнением JavaScript при вызове API для AWS S3.

У меня есть последовательность вложенных обратных вызовов, которые работают нормально до определенного вызова S3, которого мой код не ждет. Вот мой код:

getThumbUrls(contentIndex, function(data) {
  console.log('Returning from getThumbUrls');
  // let's just display thumbUrls[0] for now...
  console.log('The thumbUrls are ' + data[0]);
});

getThumbUrls() выглядит так:

function getThumbUrls(contentIndex, callback) {
  console.log('Entering getThumbUrls');

  var thumbUrls = [];

  JSON.parse(contentIndex).forEach(videoKey => {
    // get the thumbnail: bucket-name/thumbnails/<first-key>
    console.log('videoKey = ' + videoKey);

    getThumbFileName(videoKey, function(thumbFileName) {
      console.log('Returning from getThumbFileName');
      console.log('Returned thumb filename is ' + thumbFileName);

      thumbUrls.push(CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName);

    });

  });

  callback(thumbUrls);
}

А getThumbFileName() выглядит так:

function getThumbFileName(videoKey, callback) {
  console.log('Entering getThumbFileName...');

  const s3 = new AWS.S3({
    apiVersion: '2006-03-01',
    params: {
      Bucket: 'my-bucket-name'
    }
  });

  // Get the name of the file.
  params = {
    Bucket: 'my-bucket-name',
    Delimiter: '/',
    Prefix: videoKey + '/' + THUMBS_FOLDER,
    MaxKeys: 1
  };

  var urlKey;
  //console.log('listObjects params = ' + JSON.stringify(params, null, 4));
  s3.listObjectsV2(params, (err, data) => {
    if (err) {
      console.log(err, err.stack);
      callback(err);
      return;
    }

    var thumbsKey = data.Contents;
    // MaxKeys was 1 bc first thumbnail key is good enough for now. Therefore, only one iteration.
    thumbsKey.forEach(function (keys) {
      console.log('thumbKey = ' + keys.Key);
      urlKey = keys.Key;
    });

  });

  callback(urlKey);
  //callback('20161111-TheWind.jpg');
}

Очевидно, что происходит то, что выполнение не ожидает завершения вызова s3.listObjectsV2. Я проверил, что весь поток работает правильно, когда все getThumbFileName() выполняет обратный вызов с именем файла.

Может ли кто-нибудь показать мне, как заставить выполнение дождаться завершения s3.listObjectsV2, прежде чем перезвонить с undefined?

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Как уже говорилось, вы должны избегать использования метода обратных вызовов при работе с асинхронными операциями на итерациях из-за их сложности.


(Вы можете пропустить этот раздел, если не хотите знать мотивацию подхода к обещаниям).

Просто отметим, что при подходе к обратному вызову вы должны ждать завершения всех обратных вызовов в вашем getThumbUrls(), используя if, который проверит, были ли вызваны все обратные вызовы, а затем просто позвоните callback(thumbUrls); с все ответы помещены в ваш массив thumbUrls:

function getThumbUrls(contentIndex, callback) {
  const thumbUrls = [];

  // counter which will increment by one for every callback
  let counter = 0;
  JSON.parse(contentIndex).forEach(videoKey => {
    getThumbFileName(videoKey, function (thumbFileName) {
      thumbUrls.push(CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName);

      // for each callback response you must add 1 to a counter and then
      counter++;
      // check if all callbacks already has been called
      if (counter === JSON.parse(contentIndex).length) {
        // right here, thumbsUrls are filled with all responses
        callback(thumbUrls);
      }
    });
  });
}

Таким образом, вы можете использовать Обещания, и вам будет достаточно Promise.all для обработки всех ответов API. Вы можете учиться через Интернет и проверить код ниже, который использует подход обещания. Я добавил несколько комментариев, чтобы помочь вам понять, что происходит.

// when using promises, no callbacks is needed
getThumbUrls(contentIndex)
  .then(function (data) {
    console.log('Returning from getThumbUrls');
    // let's just display thumbUrls[0] for now...
    console.log('The thumbUrls are ' + data[0]);

  })

// when using promises, no callbacks is needed
function getThumbUrls(contentIndex) {
  console.log('Entering getThumbUrls');

  // not needed anymore, Promise.all will return all values
  // var thumbUrls = [];

  // Promise.all receives an array of promises and returns to next .then() all results
  // changing forEach to map to return promises to my Promise.all
  return Promise.all(JSON.parse(contentIndex).map(videoKey => {
    console.log('videoKey = ' + videoKey);

    // returning a promise
    return getThumbFileName(videoKey)
      .then(function (thumbFileName) {
        console.log('Returning from getThumbFileName');
        console.log('Returned thumb filename is ' + thumbFileName);

        return CLOUDFRONT_URL + videoKey + '/thumbnails/' + thumbFileName;
      });
  }))
}

// when using promises, no callbacks is needed
function getThumbFileName(videoKey) {
  console.log('Entering getThumbFileName...');

  const s3 = new AWS.S3({
    apiVersion: '2006-03-01',
    params: {
      Bucket: 'my-bucket-name'
    }
  });

  // Get the name of the file.
  params = {
    Bucket: 'my-bucket-name',
    Delimiter: '/',
    Prefix: videoKey + '/' + THUMBS_FOLDER,
    MaxKeys: 1
  };

  // urlKey not need anymore
  // var urlKey;

  // most of AWS functions has a .promise() method which returns a promise instead calling callback funcions
  return s3.listObjectsV2(params).promise()
    .then(function (data) {
      var thumbsKey = data.Contents;
      //if you want to return only first one thumbsKey:
      return thumbsKey[0];
    })
    .catch(function (err) {
      console.log(err, err.stack);
      callback(err);
      return;
    })
}

Надеюсь, это поможет вам в учебе.

0 голосов
/ 16 января 2019

Может ли кто-нибудь показать мне, как заставить исполнение ждать

Это неправильный вопрос. Вы не пытаетесь заставить казнь «ждать» или, по крайней мере, не должны этого делать. Вам просто нужно вызвать обратный вызов в нужном месте - внутри обратного вызова с s3.listObjectsV2(), а не снаружи.

function getThumbFileName(videoKey, callback) {
  ...
  s3.listObjectsV2(params, (err, data) => {
    if (err) {
      ...
    }

    var thumbsKey = data.Contents;
    // MaxKeys was 1 bc first thumbnail key is good enough for now. Therefore, only one iteration.
    thumbsKey.forEach(function (keys) {
      console.log('thumbKey = ' + keys.Key);
      urlKey = keys.Key;
    });

    callback(urlKey); // right
  });

  // wrong // callback(urlKey);

}

Как вы написали, обратный вызов срабатывает после s3.getObjectsV2() начинает запуск, а не после того, как завершает (вызывает свой собственный обратный вызов).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...