От новичка до гуру: Курсы программирования на CyberDuff

Синхронная функция Javascript с обещанием не работает должным образом

У меня есть следующие функции, которые я бы предпочел обрабатывать синхронно, но почему-то они не работают должным образом.

function upload_to_aws(data) {
  return new Promise(function(resolve, reject) {
    loan_application_id = $('#loan_application_id').val();

    var s3BucketName = data.bucket_name;
    var s3RegionName = data.region;
    AWS.config.update({accessKeyId: data.key, secretAccessKey: data.secret_key, region: s3RegionName});
    var s3 = new AWS.S3({params: {Bucket: s3BucketName, Region: s3RegionName}});

    aws_url= []
    $('.attached_image').each(function() {
      if($(this).attr('src') != "/assets/upload_bg.png" && $(this).attr('src') != '' ) {
        var timestamp = (new Date()).getTime();
        var randomInteger = Math.floor((Math.random() * 1000000) + 1);
        filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp  + '.png';
        var u = $(this).attr('src').split(',')[1],
          binary = atob(u),
          array = [];

        for (var i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
        }

        var typedArray = new Uint8Array(array);
        s3_upload(s3, filename, typedArray).then(function(url_aws) {
          aws_url.push(url_aws);
          console.log(aws_url)
          console.log(aws_url.length)
        })
      }
    })
    resolve(aws_url);
  })
}

function s3_upload(s3, filename, typedArray) {
  return new Promise(function(resolve, reject) {
    s3.putObject({Key: filename, ContentType: 'image/png', Body: typedArray.buffer, ContentEncoding: 'base64', ACL: 'public-read'},
    function(err, data) {
        if (data !== null) {
          url_aws = s3.endpoint.href + filename;
          resolve(url_aws)
        }
        else {
          reject(err);
        }
    });
  })
}

Когда эта функция вызывается, она вызывает функцию upload_to_aws, и я хочу, чтобы все было выполнено в этой функции, прежде чем она вернет мне массив URL-адресов aws_uploaded.

$.when(upload_to_aws(data.data)).then(function(aws_uploaded_url) {
   console.log(aws_uploaded_url);
})

Но что в основном происходит в данный момент, так это то, что во время загрузки изображения в s3 это вызывается resolve(aws_url) еще до того, как изображения загружены в s3, поэтому это печатает console.log(aws_uploaded_url) как пустой массив [], потому что функция не выполнена полностью.

Есть ли другой способ обработки обратных вызовов и синхронных функций в javascript?


  • Вы должны поместить resolve(aws_url); внутрь функции then обещания s3_upload. 12.06.2018

Ответы:


1

Основная проблема заключается в том, что код не ждет, пока обещания, возвращенные s3_update, будут обработаны и заполнит массив aws_url, прежде чем разрешить обещание, возвращенное upload_to_aws, с этим массивом (он все еще пуст).

До сих пор это часто задаваемый вопрос, рассматриваемый в Как сделать Я возвращаю ответ от асинхронного вызова?, но подставляя метод s3.putObject вместо вызова AJAX.

Вы также хотите ожидать несколько (ноль или более промисов), поскольку количество запросов определяется данными. Ожидание завершения нескольких промисов включает использование Promise.all.

Выполненное значение вызова Promise.all представляет собой массив выполненных значений промисов, предоставленных в качестве аргументов, в порядке предоставления аргументов. Другими словами, выполненное значение — это массив aws_url, используемый в сообщении.

Можно попробовать (непроверенный) подход с небольшими изменениями для объявления всех переменных и упрощения s3_upload:

function upload_to_aws(data) {
    var loan_application_id = $('#loan_application_id').val();
    var s3BucketName = data.bucket_name;
    var s3RegionName = data.region;
    AWS.config.update({accessKeyId: data.key, secretAccessKey: data.secret_key, region: s3RegionName});
    var s3 = new AWS.S3({params: {Bucket: s3BucketName, Region: s3RegionName}});

    var urlPromises = [];
    $('.attached_image').each(function() {
      if($(this).attr('src') != "/assets/upload_bg.png" && $(this).attr('src') != '' ) {
        var timestamp = (new Date()).getTime();
        var randomInteger = Math.floor((Math.random() * 1000000) + 1);
        var filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp  + '.png';
        var u = $(this).attr('src').split(',')[1];
        var binary = atob(u);
        var array = [];
        for (var i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
        }
        var typedArray = new Uint8Array(array);
        urlPromises.push( s3_upload(s3, filename, typedArray))
      }
    });
    return Promise.all( urlPromises);
}

function s3_upload(s3, filename, typedArray) { // promisify a call back
  return new Promise(function(resolve, reject) {
    s3.putObject(
        {Key: filename, ContentType: 'image/png', Body: typedArray.buffer, ContentEncoding: 'base64', ACL: 'public-read'},
        function(err, data) { err ? reject(err) : resolve( data);}
    );
  });
}

(Если какая-либо из объявленных переменных должна быть глобальной, удалите var перед ними).

Обещание, возвращаемое вызовом upload_to_aws, должно быть выполнено с массивом из нуля или более загруженных URL-адресов:

$.when(upload_to_aws(data.data)).then(function(aws_uploaded_urls {
    console.log(aws_uploaded_urls);
})


Совместимость с JQuery (обновление)

До версии 3 jQuery не реализовывал обещания, соответствующие спецификации обещаний Aplus или более поздний стандарт ECMAScript версии 6. Старые версии JQuery способны вообще не распознавать обещания ES6 как обещания и не могут дождаться их выполнения.

Убедитесь, что вы используете JQuery 3 или более позднюю версию с кодом, который использует собственные промисы. Если вам нужно поддерживать браузеры IE, вам также потребуется включить полифилл для промисов в стиле ES6, который поддерживает Promise.all.

Если вам необходимо поддерживать браузеры, которые больше не поддерживаются в JQuery 3, рассмотрите возможность полное удаление использования Promise и, скажем, рефакторинг кода с использованием объектов Deferred (за рамками этого ответа). Это также устранит необходимость в полифилле в старых браузерах, в которых отсутствует встроенная поддержка Promise.

Если метод .when создает проблемы в сочетании с использованием промисов ES6, рассмотрите возможность вызова кода в простом JavaScript:

upload_to_aws(data.data)
.then(function(aws_uploaded_urls) {
    console.log(aws_uploaded_urls);
})
.catch( function( err) {
   console.log( "upload_to_aws failed: ", err);
}
12.06.2018

2

Поскольку каждая итерация выполняет что-то асинхронное, вместо этого вам нужно использовать Promise.all, сопоставляя каждую итерацию с Promise и, наконец, разрешая промис, возвращаемый upload_to_aws, после завершения всех промисов итераций:

function upload_to_aws(data) {
    loan_application_id = $('#loan_application_id').val();

    var s3BucketName = data.bucket_name;
    var s3RegionName = data.region;
    AWS.config.update({accessKeyId: data.key, secretAccessKey: data.secret_key, region: s3RegionName});
    var s3 = new AWS.S3({params: {Bucket: s3BucketName, Region: s3RegionName}});

    return Promise.all(
      [...document.querySelectorAll('.attached_image')]
      .map(image => new Promise((resolve, reject) => {
        const { src } = image;
        if(src != "/assets/upload_bg.png" && src != '' ) {
          var timestamp = (new Date()).getTime();
          var randomInteger = Math.floor((Math.random() * 1000000) + 1);
          filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp  + '.png';
          var u = src.split(',')[1],
              binary = atob(u),
              array = [];

          for (var i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
          }

          var typedArray = new Uint8Array(array);
          s3_upload(s3, filename, typedArray).then(resolve, reject);
        }
      }))
  );
}
12.06.2018
  • Вы можете предпочесть эту последнюю строку как s3_upload(s3, filename, typedArray).then(resolve, reject); для передачи ошибок 12.06.2018
  • @CertainPerformance, это выглядит великолепно, но все же печатает Promise <pending>, и часть разрешения выполняется до того, как функция завершает выполнение для этой части. $.when(upload_to_aws(data.data)).then(function(aws_uploaded_url) { console.log(aws_uploaded_url); }) 12.06.2018
  • @KingsleySimon Вы уверены, что точно скопировали код? В начале функции с моим не создается явного Promise. Вы можете рассмотреть возможность использования только нативных промисов, а не добавления jQuery в микс - например, upload_to_aws(data.data).then(aws_uploaded_url => console.log(aws_uploaded_url)) 12.06.2018
  • @KingsleySimon К сожалению, я не могу воспроизвести проблему. Это сам aws_uploaded_url выглядит как Promise или это массив Promise? 12.06.2018
  • Новые материалы

    Представляем Narwhal Technologies (Nrwl)
    6 декабря 2016 г. Маунтин-Вью, Калифорния С тех пор, как Виктор Савкин и я (Джефф Кросс) присоединились к команде Angular в Google на заре Angular 1, Angular продемонстрировал феноменальный..

    Путь AWS  — «Изучение машинного обучения — 10 начинающих ИИ и машинного обучения на AWS».
    Универсальный ресурсный центр для изучения искусственного интеллекта и машинного обучения. НОЛЬ или ГЕРОЙ, начните свое путешествие здесь. Получите решения и пройдите обучение у экспертов AWS...

    5 простых концепций Python, ставших сложными
    #заранее извините 1) Переменные x = 4 y = 5 Переменная в Python — это символическое представление объекта. После присвоения некоторого объекта переменной Python мы приобретаем..

    «Освоение вероятности: изучение совместной, предельной, условной вероятности и теоремы Байеса —…
    Виды вероятности: Совместная вероятность Предельная вероятность Условная вероятность Диаграмма Венна в вероятностях: В “Set Theory” мы создаем диаграмму Венна...

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..