Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 18.08.2021, 12:12
Новичок на форуме
Отправить личное сообщение для zloy_ej Посмотреть профиль Найти все сообщения от zloy_ej
 
Регистрация: 18.08.2021
Сообщений: 4

async в анонимной функции
Добрый день, возник вопрос, который я не могу решить своими силами. Есть скрипт:
'use strict';
var btn = $('.form_submit');
function AJAX() {
    return new Promise(function(resolve, reject){
        $.ajax({
        type: "POST",
        url: "/get_building",
        data: $('form').serialize(),
        type: 'POST',
        success: function(response) {
            var json = jQuery.parseJSON(response);  //Прочитать ответ сервера
            var resp_list = json.resp; //массив с ключем par_buld_resp (из функции get_building main.py)
            console.log(`Это resp_list ${resp_list}`);
            resolve(resp_list);
        },
        error: function(error) {
          console.log(error);
          reject(error);
        }
    });
    })
}

async function checkForm() {
    let promise = await AJAX();
    var title = $('.add_building_form').val();
    console.log(`это промис ${promise}`);
    console.log(`это title ${title}`);
    return(false);
}

$(document).ready(function(){
    btn.click(function(){
        let answer =  checkForm();
        console.log(`Это answer ${answer}`);
        if(answer==false) {
            
            return false;
        }
        else
            return false;
        //{form.submit();}
    })
})


который при клике по кнопке запускает функцию checkForm. Та в свою очередь запускает другую функцию, которая через AJAX обращается к бд и проверяет, есть ли уже запись в БД с таким названием. checkForm принимает промис от функции AJAX в котором либо old (то есть запись уже есть), либо new - если записи нет, а так же проверяет введенную строку (пока не реализовал) и возвращает промис (пока только false). В функциях расставлены логи, вот вывод:
Это answer [object Promise]
Это resp_list Old
это промис Old
это title 123
Из всего этого становится понятно, что основная анонимная функция, которая ждет нажатия на кнопку не ждет пока checkForm выполнится. Вопрос: как сделать анонимную функцию асинхронной?
если я делаю так:
btn.click(async function(){
        let answer =  await checkForm();
        ....

выскакивает ошибка интерпретатора uncaught exception: Object
Ответить с цитированием
  #2 (permalink)  
Старый 18.08.2021, 12:25
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,593

Должно работать, ошибка где-то в другом месте.

Разве что return false не сработает, потому что async функция возвращает не значение, а Promise отдающий значение. Соответственно, форма попытается отправиться.

Чтобы остановить действие click - ты должен его остановить прямо сейчас, асинхронная же функция выполнится когда-нибудь потом.
Решение: останавливать событие в любом случае, а уже потом, когда отработает асинхронная функция - триггерить нужное действе вручную.
__________________
29375, 35

Последний раз редактировалось Aetae, 18.08.2021 в 12:32.
Ответить с цитированием
  #3 (permalink)  
Старый 18.08.2021, 12:52
Аватар для SuperZen
Профессор
Отправить личное сообщение для SuperZen Посмотреть профиль Найти все сообщения от SuperZen
 
Регистрация: 08.11.2017
Сообщений: 641

<input type="button" class="form_submit" value="click" />
<script>
document.querySelector('.form_submit').addEventListener('click', async function() {
  const result = await Promise.resolve('ok');
  console.log(result);
});
</script>
Ответить с цитированием
  #4 (permalink)  
Старый 18.08.2021, 16:04
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,763

let promise = await AJAX();

Функция AJAX возвращает promise. Но await AJAX() вернет только результат успешного разрешения промиса, т.е. resp_list. error в случае неудачи она не вернет.

let answer = checkForm();

checkForm описана как async. Она возвращает промис, который разрешается, как false.
Что бы получить этот самый false, ее надо вызывать
let answer = await checkForm();


выскакивает ошибка интерпретатора uncaught exception: Object

Возможно потому, что в ajax срабатывает error, но он не ловится await.
Попробуйте вызывать так

try {
   let promise = await AJAX();
    var title = $('.add_building_form').val();
    console.log(`это промис ${promise}`);
    console.log(`это title ${title}`);
} catch (error) {
   console.log('Error', error)
}
    return(false);
Ответить с цитированием
  #5 (permalink)  
Старый 19.08.2021, 13:57
Новичок на форуме
Отправить личное сообщение для zloy_ej Посмотреть профиль Найти все сообщения от zloy_ej
 
Регистрация: 18.08.2021
Сообщений: 4

Немного переделал код, получилось вот так:
'use strict';
var btn = document.querySelector('.form_submit');
function AJAX() {
    return new Promise(function(resolve, reject){
        $.ajax({
        type: "POST",
        url: "/get_building",
        data: $('form').serialize(),
        type: 'POST',
        success: function(response) {
            console.log(`Это response ${response}`);
            var json = jQuery.parseJSON(response);  //Прочитать ответ сервера
            var resp_list = json.resp; //массив с ключем par_buld_resp (из функции get_building main.py)
            console.log(`Это resp_list ${resp_list}`);
            resolve(resp_list);
        },
        error: function(error) {
          console.log(`Это error в AJAX ${error.name}`);
          reject(error);
        }
    });
    })
}

async function checkForm() {
    var title = $('.add_building_form').val();
        if (title == '') {
            return "No"
        }
    try {
        let promise = await AJAX();        
        console.log(`это промис ${promise}`);
        console.log(`это title ${title}`);
        if (promise == 'Old') {
            return "No"
            }
        else return "Yes";
        
    } catch (error) {
        console.log(`Error ${error}`);
    }
    return("No");
}

      
btn.addEventListener('click', async function() {
const answer =  await checkForm();
console.log(`Это answer ${answer}`);
if (answer == "No") {
    return false;
}
else {
    return btn.submit();
}
});

При вводе пустой строки, как и полагается запрос ловится еще в функции checkForm с выводом в консоль:
Это answer No
При вводе каких либо значений, затык происходит в функции AJAX со следующим логом:
Это error в AJAX undefined
Error [object Object]
Это answer No
причем на сервере (Flask) обращения не приходит.

Если использовать старый код:
$(document).ready(function(){
    var btn = $('.form_submit');
    btn.click(function(){
      var answer = AJAX();
      var title = $('.add_building_form').val();
      if(title==''){
        return false;
      }
      else
        return false;
        //{form.submit();}
    })
})

function AJAX() {
    $.ajax({
        type: "POST",
        url: "/get_building",
        data: $('form').serialize(),
        type: 'POST',
        success: function(response) {
            var json = jQuery.parseJSON(response);  //Прочитать ответ сервера
            var resp_list = json.resp; //массив с ключем par_buld_resp (из функции get_building main.py)
            console.log(resp_list);
            return resp_list;
        },
        error: function(error) {
          console.log(error);
          return error;
        }
    });
}


ответ приходит, AJAX отрабатывает, resp_list в зависимости от того, что ввел получает значение New или Old. Но так как асинхронность не прописана, анонимная функция, вызывающая AJAX() получает undefined (оно понятно, так как анонимная выполняется дальше а значение в ответе еще не определено).

Не могу понять, чем первый AJAX отличается от второго, затык именно в этой функции, но найти его я не могу (
Ответить с цитированием
  #6 (permalink)  
Старый 19.08.2021, 16:41
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,593

Цитата:
ввел получает значение New или Old
Нет.
Если succes - возвращает (в никуда) значение New.
Если error - ничего не делает.

В новом варианте:
Если succes - возвращает New.
Если error - кидает ошибку.
Чтоб не кидало ошибку - не надо кидать ошибку. На error верни вместо этого руками resolve('No'); вместо reject(error);.

Если при ошибке сервера таки должна быть ошибка, а не No, то поправь сервер, чтоб при возврате No он не ставил в код ответа ошибку.
__________________
29375, 35
Ответить с цитированием
  #7 (permalink)  
Старый 22.08.2021, 15:41
Новичок на форуме
Отправить личное сообщение для zloy_ej Посмотреть профиль Найти все сообщения от zloy_ej
 
Регистрация: 18.08.2021
Сообщений: 4

Решил сделать так:
'use strict';

const requestURL = '/get_building';
let frm = document.forms[0];
frm.onsubmit = async function(event) {
    let body = 'building_name=' + frm.building_name.value;    //получаем 'building_name=123', Если ввели в форму 123
    let answer = AJAX("POST", requestURL, body)
    event.preventDefault();
    answer.then(data => {
        if(data.resp=='New'){
            console.log('Новое здание');
        }
        else {
            console.log('Старое здание');
            
        }
    })
    .catch(err => console.log(err))
}

function AJAX(method, url, body = null) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url);
        xhr.responseType = 'json';
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.onload = () => {
            if (xhr.status >= 400) {
                reject(xhr.response)
            } else {
                resolve(xhr.response)
            }
        }
        xhr.onerror = () => {
            reject(xhr.response)
        }
        xhr.send(body)
    })
    
}

Запрос отправляется и приходит ответ. Мы его выводим в консоль. То, что было введено в форму сравнивается с тем, что есть в БД.
Не могу сделать так, что бы если запись уже есть и срабатывает условие
if(data.resp=='New'){
            console.log('Новое здание');
        }

запрос отправлялся. Сейчас он блокируется вот этим:
event.preventDefault();

Если сделать вот так:
const requestURL = '/get_building';
let frm = document.forms[0];
frm.onsubmit = async function(event) {
    let body = 'building_name=' + frm.building_name.value;    //получаем 'building_name=123', Если ввели в форму 123
    let answer = AJAX("POST", requestURL, body)
    
    answer.then(data => {
        if(data.resp=='New'){
            console.log('Новое здание');
        }
        else {
            return event.preventDefault();
            console.log('Старое здание');
            
        }
    })
    .catch(err => console.log(err))
}

то запрос все равно отправляется. Почему так происходит, и как надо?
Ответить с цитированием
  #8 (permalink)  
Старый 22.08.2021, 22:25
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,763

Сообщение от zloy_ej
то запрос все равно отправляется. Почему так происходит, и как надо?
Потому, что выход из frm.onsubmit происходит раньше, чем закончится
AJAX("POST", requestURL, body) и запрос уже будет отпроавлен. А event.preventDefault() будет выполняться уже потом
Не знаю вашего html кода, но сделать можно так - вместо кнопки <input type=submit> использовать <button type=button> и вместо frm.onsubmit

const requestURL = '/get_building';
let frm = document.forms[0];
let but = ...// наша кнопка
but.onclick = async function(event) {
    let body = 'building_name=' + frm.building_name.value;    //получаем 'building_name=123', Если ввели в форму 123
    let answer = AJAX("POST", requestURL, body)
     
    answer.then(data => {
        if(data.resp=='New'){
            console.log('Новое здание');
            frm.submit()
        }
        else {
            console.log('Старое здание');
             
        }
    })
    .catch(err => console.log(err))
}
Ответить с цитированием
  #9 (permalink)  
Старый 23.08.2021, 12:43
Новичок на форуме
Отправить личное сообщение для zloy_ej Посмотреть профиль Найти все сообщения от zloy_ej
 
Регистрация: 18.08.2021
Сообщений: 4

Большое спасибо всем принимавшим участие в обсуждении сабжа. Задача решена. Дальше буду навешивать функционал и облагораживать код, но это уже другая история! ) Выкладываю то, что получилось, возможно кому нибудь пригодится.
Html:
{% extends 'base.html' %}

{% block content %}
    <p><h3 class="helper">Добавляются здания, сооружения или территория предприятия</h3></p>
    {% for cat, msg in get_flashed_messages(True) %}
    <div class="flash {{cat}}">{{msg}}</div>
    {% endfor %}
    <form action="/add_building" method="post" class="form_add">
        <p><label>Название: </label> <input type="text" name="building_name" value="" class='add_building_form' placeholder="Введите название здания" required></p>
        <p><button type="button" value="Добавить" class="form_submit">Отправить</button></p>
    </form>
    <!--<script src="{{url_for('static', filename='js/add_building.js') }}"></script>-->
    <script src="{{url_for('static', filename='js/proba.js') }}"></script>
    <!--<script src="{{url_for('static', filename='js/execise.js') }}"></script>-->
    <!--<script src="{{url_for('static', filename='js/ex1.js') }}"></script>-->
{% endblock %}


бэк построен на фласке, там тоже не все гладко, но там наведу порядок со временем, вот участок с обработчиком:
Код:
@app.route('/get_building', methods=['GET', 'POST'])
def get_building():
    print(request.form)
    build_name = request.form['building_name']
    db = get_db()
    mydbase = Building(db)
    building_id = mydbase.getBuilding(build_name, ALL=False)
    print('new_building', build_name)
    print('building_id', building_id)
    if (building_id):
        print('Уже есть')
        return json.dumps({'resp': "Old"})
    else:
        print('Новое здание')
        return json.dumps({'resp': "New"})
Далее собственно JS:
'use strict';

const requestURL = '/get_building';
let frm = document.forms[0];
let but = frm.elements[1];
but.onclick = async function(event) {
    let body = 'building_name=' + frm.building_name.value;    //получаем 'building_name=123', Если ввели в форму 123
    
    let answer = AJAX("POST", requestURL, body)
    answer.then(data => {
        if(data.resp=='New'){
            console.log('Новое здание');
            frm.submit();
        }
        else {
            console.log('Старое здание');
        }
    })
    .catch(err => console.log(err))
}

function AJAX(method, url, body = null) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url);
        xhr.responseType = 'json';
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.onload = () => {
            if (xhr.status >= 400) {
                reject(xhr.response)
            } else {
                resolve(xhr.response)
            }
        }
        xhr.onerror = () => {
            reject(xhr.response)
        }
        xhr.send(body)
    })
    
}


Знаю что кривенько написано, но в конце концов я только учусь!)
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Возврат значения из функции async dmitri22 Общие вопросы Javascript 2 18.07.2019 20:31
Документирование анонимной функции (JSDoc) NO_ONE Библиотеки/Тулкиты/Фреймворки 0 18.07.2018 18:52
Полифилл в анонимной функции NO_ONE Общие вопросы Javascript 1 14.07.2018 12:07
распарсить строку json sotik AJAX и COMET 14 24.11.2014 16:06
callback для анонимной функции gh321 jQuery 2 18.11.2013 13:20