Javascript.RU

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

Не могу разобраться с промисами
Только недавно сел разбираться с промисами, выглядит все довольно сложно и непонятно. Казалось бы только уловил суть, написал код в соответсвие со своим понимание, но не работает.
Точнее работает, но нет асинхронности.
function check(input) {
let err = true;
let email_promise = new Promise(function(resolve, reject) {
			let xml = new XMLHttpRequest();
			xml.open("POST", "script/LoginCheck.php", true);
			xml.send();
			xml.onreadystatechange = function() {
				if (xml.readyState == 4 && xml.status == 200) {
					let rm = /^[\w\.\d-_]+@[\w\.\d]+\.[\w\.\d]+$/i;
					if (xml.responseText == true && rm.test(input.value)) resolve();
					else reject();
				}
			}
		});
		email_promise.then(function() {
			err = true;
		},
		function() {
			err = false;
			alert("Такой адрес электронной почты уже зарегистрирован");
		});
return err;
}

По задумке этот кусок кода проверяет 1) Действительно ли значение в input является почтой, 2) Что такой адрес еще не зарегистрирован (проверка через запрос на LoginCheck.php).
И впринципе при однократном вызове (при заполнении соответствующего поля) все отрабатывает правильно. Однако дальше, при завершении регистрации, возникает проблема:
var registrationDATA = new FormData();
function Registration() {
  let inputs = document.querySelectorAll("input");
  let err = true;
  for (let input of inputs) {
	if (!check(input)) err = false;
	else registrationDATA.set(input.name, input.value);
  }
  if (err) alert("Не все поля заполнены корректно");
}

Почему-то в последнем условии выполняется именно вариант else при чем промис отрабатывает так, словно работает синхронно (т.е. сначала завершается выполнение функции Registration, а затем появляется сообщение "Такой адрес уже зарегистрирован")

P.S. Аналогично пробовал реализовать вызов функции check через промис в последнем условии, но с тем же результатом.
var registrationDATA = new FormData();
function Registration() {
  let inputs = document.querySelectorAll("input");
  let err = true;
  for (let input of inputs) {
	let check_promise = new Promise(function(resolve, reject) {
			if (!check(input)) resolve(true);
			else registrationDATA.set(input.name, input.value);
		});
		check_promise.then(function(result) {err = result;});
  }
  if (err) alert("Не все поля заполнены корректно");
}


P.P.S. Да, там ошибка в логике "Такой адрес зарегистрирован" будет появляться даже в случае, когда строка просто не является адресом электронной почты. Но это не влияет на описанную проблему.
Ответить с цитированием
  #2 (permalink)  
Старый 10.02.2021, 23:04
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,762

Строка 22 return err;
будет выполняться синхронно и всегда возвращать true
Асинхронно будут выполняться только функции в стр. 15-17 в случае успеха и 18-21 в случае ошибки.

В асинхронном программировании приходится делать асинхронным все, что зависит от первой асинхронной операции
Поэтому функция chek должна возвращать не конкретное значение, а свой промис

function check(input) {
return new Promise(function(resolve, reject) {
            let xml = new XMLHttpRequest();
            xml.open("POST", "script/LoginCheck.php", true);
            xml.send();
            xml.onreadystatechange = function() {
                if (xml.readyState == 4 && xml.status == 200) {
                    let rm = /^[\w\.\d-_]+@[\w\.\d]+\.[\w\.\d]+$/i;
                    if (xml.responseText == true && rm.test(input.value)) resolve();
                    else reject();
                }
            }
        })
        .then(
			function() {
				return true;
			},
			function() {
				alert("Такой адрес электронной почты уже зарегистрирован");
				return false
			}
        );
}


и вызывать ее
check(input).then (function (result) {/*result будет true или false */})

Или в асинхронной функции
let result = await check(input)
/*result будет true или false тут его обрабатываем*/

Последний раз редактировалось voraa, 10.02.2021 в 23:08.
Ответить с цитированием
  #3 (permalink)  
Старый 11.02.2021, 00:26
Новичок на форуме
Отправить личное сообщение для asdffak Посмотреть профиль Найти все сообщения от asdffak
 
Регистрация: 17.01.2017
Сообщений: 7

Блин, как сложна та!
В любом случае спасибо за помощь.

Интересно, а есть вариант решить ту же задачу без промисов? Или это самый не костыльный вариант?
Потому что на мой взгляд код выглядит как-то перегруженно и трудно читаем.
Кроме того задача оказывается нескольо сложнее, поскольку я привел лишь часть кода и рально функция check проверяет вообще все input'ы согласно их имени, но запрос на сервер предусмотрен только в одном случае:
function check(input) {
let err = true;
switch(input.name) {
case "email":
let email_promise = new Promise(function(resolve, reject) {
			let xml = new XMLHttpRequest();
			xml.open("POST", "script/LoginCheck.php", true);
			xml.send();
			xml.onreadystatechange = function() {
				if (xml.readyState == 4 && xml.status == 200) {
					let rm = /^[\w\.\d-_]+@[\w\.\d]+\.[\w\.\d]+$/i;
					if (xml.responseText == true && rm.test(input.value)) resolve();
					else reject();
				}
			}
		});
		email_promise.then(function() {
			err = true;
		},
		function() {
			err = false;
			alert("Такой адрес электронной почты уже зарегистрирован");
		});
break;

case "..."
  if(...) ...;
  else err = false;
break

case "..."
break;
......

return err;
}

Т.е. в каждом кейсе выполняется проверка и, если поле его не проходит, то err = false.
Это что, получается мне нужно будет все кейсы оборачивать во внутренний промис?

Последний раз редактировалось asdffak, 11.02.2021 в 00:39.
Ответить с цитированием
  #4 (permalink)  
Старый 11.02.2021, 08:01
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,762

Сообщение от asdffak
Это что, получается мне нужно будет все кейсы оборачивать во внутренний промис?
Как бы да.
Но для этого существуют асинхронные функции (async/await)
Функция объявленная async всегда возвращает промис. Если ей в return указать промис, она его и вернет. А если какое то другое значение, то она обернет его в Promise.resolve(value)

await получает промис, ждет его разрешение и вытаскивает значение, которое было передано через resolve. Неудобно только, что значение переданное через reject так не получить. поэтому их приходится ловить с помощью try{} catch. Но через reject лучше передавать настоящие ошибки.
Ваш код можно переписать так
async function check(input) {
let err = true;
switch(input.name) {
case "email":
return new Promise(function(resolve, reject) {
            let xml = new XMLHttpRequest();
            xml.open("POST", "script/LoginCheck.php", true);
            xml.send();
            xml.onreadystatechange = function() {
                if (xml.readyState == 4 && xml.status == 200) {
                    let rm = /^[\w\.\d-_]+@[\w\.\d]+\.[\w\.\d]+$/i;
                    if (xml.responseText == true && rm.test(input.value)) resolve(true);
                    else resolve (false);
                }
            }
            xml.onerror = function() { reject () }

        });
        .then(function(err) {    
				if (err) alert("Такой адрес электронной почты уже зарегистрирован");
				return err
			}
        );
break;
 
case "..."
  if(...) ...;
  else err = false;
break
 
case "..."
break;
......
 
return err;
}

var registrationDATA = new FormData();

async function Registration() {
  let inputs = document.querySelectorAll("input");
  let err = false;
  for (let input of inputs) {
	err =  await check(input)
	if (err) break;
    registrationDATA.set(input.name, input.value);
  }
  if (err) alert("Не все поля заполнены корректно");
}

// Вызов Registration
try {
	await Registration()
catch () {
	alert("Ошибка соединения с сервером");
}
Ответить с цитированием
  #5 (permalink)  
Старый 13.02.2021, 01:02
Новичок на форуме
Отправить личное сообщение для asdffak Посмотреть профиль Найти все сообщения от asdffak
 
Регистрация: 17.01.2017
Сообщений: 7

Еще раз спасибо.
Мне вот стало интересно: а обязательно в последнем варианте функцию Registration объявлять через async?

И еще немного непонятен вот этот момент:
});
        .then(function(err) {   
                if (err) alert("Такой адрес электронной почты уже зарегистрирован");
                return err
            }
        );

В первой же стоке завершилось описание промиса. К чему тогда вызвается метод then? Выглядит как будто к переменной с пустым именем.

Последний раз редактировалось asdffak, 13.02.2021 в 01:10.
Ответить с цитированием
  #6 (permalink)  
Старый 13.02.2021, 08:16
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,762

Сообщение от asdffak
Мне вот стало интересно: а обязательно в последнем варианте функцию Registration объявлять через async?
Да. await можно использовать только в async функциях. По синтактическим правилам только в них await является ключевым словом.
Это check можно было без async объявить, т.к она сама по себе возвращает промис.

Сообщение от asdffak
И еще немного непонятен вот этот момент:
Можно было и не делать так. Просто сделал, что бы именно тут выдать сообщение, что такой адрес уже есть.
Просто дальше уже невозможно будет понять из-за какого поля err стал true.

Последний раз редактировалось voraa, 13.02.2021 в 08:34.
Ответить с цитированием
Ответ



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

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


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Не могу разобраться с closest() konst37 Общие вопросы Javascript 4 13.06.2017 01:09
Не могу разобраться с валидатором форм ПавелСедой Общие вопросы Javascript 4 14.02.2017 18:22
PHP+JQuery, не могу разобраться помогите Nirfik Events/DOM/Window 2 13.11.2014 22:56
Не могу разобраться в коде Карбонат jQuery 17 22.08.2014 11:53
Не могу разобраться с setTimeout bazilio2010 Общие вопросы Javascript 3 14.01.2012 00:17