Я хочу использовать обещание для обработки модального окна, чтобы при вызове модального окна с помощью синтаксиса await
синхронное выполнение вызывающей функции приостанавливалось до тех пор, пока пользователь не ответит на модальное окно. Приведенный ниже фрагмент кода извлекает основные элементы проблемы. Хотя он работает, я не уверен, является ли это обещанным антипаттерном или я представляю скрытые сложности, если в обработчиках onclick
возникают ошибки. Ближайшие вопросы и ответы, которые я смог найти ( Разрешить обещание в более позднее время ), не совсем отвечают моей проблеме, так как ответы, похоже, не относятся к обещанию, которое хранится в резерве в ожидании события пользователя ...
Мой урезанный Modal
класс и пример выполнения включают в себя следующие ключевые элементы ...
- class
Modal
создает модальные элементы DOM и добавляет их к HTML документ. - класс
Modal
имеет метод с именем show
, который показывает модальное значение (в этом упрощенном примере три кнопки) и устанавливает обещание. Функции resolve
и reject
обещания затем сохраняются как атрибуты Modal
экземпляра, в частности resolveFunction
и rejectFunction
. - Только когда пользователь нажимает Okay, Cancel или CancelThrow, Обещание разрешено или отклонено.
- Функция
openModal
- это функция, которая устанавливает и показывает модальное состояние, а затем приостанавливает ожидание разрешения обещания, созданного методом модального show()
.
<html><head>
<style>
#ModalArea {
display: none;
}
#ModalArea.show {
display: block;
}
</style>
<script>
class Modal {
constructor() {
this.parentNode = document.getElementById( 'ModalArea' );
let okay = document.createElement( 'BUTTON' );
okay.innerText = 'Okay';
okay.onclick = ( event ) => {
this.resolveFunction( 'Okay button clicked!' )
};
this.parentNode.appendChild( okay );
let cancel = document.createElement( 'BUTTON' );
cancel.innerText = 'Cancel';
cancel.onclick = ( event ) => {
this.rejectFunction( 'Cancel button clicked!' )
};
this.parentNode.appendChild( cancel );
let cancelThrow = document.createElement( 'BUTTON' );
cancelThrow.innerText = 'Cancel w/Throw';
cancelThrow.onclick = ( event ) => {
try {
throw 'Thrown error!';
} catch( err ) {
this.rejectFunction( err );
}
this.rejectFunction( 'CancelThrow button clicked!' );
};
this.parentNode.appendChild( cancelThrow );
}
async show() {
this.parentNode.classList.add( 'show' );
// Critical code:
//
// Is this appropriate to stash away the resolve and reject functions
// as attributes to a class object, to be used later?!
//
return new Promise( (resolve, reject) => {
this.resolveFunction = resolve;
this.rejectFunction = reject;
});
}
}
async function openModal() {
// Create new modal buttons...
let modal = new Modal();
// Show the buttons, but wait for the promise to resolve...
try {
document.getElementById( 'Result' ).innerText += await modal.show();
} catch( err ) {
document.getElementById( 'Result' ).innerText += err;
}
// Now that the promise resolved, append more text to the result.
document.getElementById( 'Result' ).innerText += ' Done!';
}
</script>
</head><body>
<button onclick='openModal()'>Open Modal</button>
<div id='ModalArea'></div>
<div id='Result'>Result: </div>
</body></html>
Есть ли подводные камни в способе обработки функций resolve
и reject
, и если да, то есть ли лучший шаблон проектирования для обработки этого варианта использования? ?
РЕДАКТИРОВАТЬ
Основываясь на рекомендациях Roamer-1888, я пришел к следующей более чистой реализации отложенного обещания ... (Обратите внимание, что проверка Cancel w/Throw
приводит к тому, что консоль показывает ошибку Uncaught (in Promise)
, но обработка продолжается, как определено ...)
<html><head>
<style>
#ModalArea {
display: none;
}
#ModalArea.show {
display: block;
}
</style>
<script>
class Modal {
constructor() {
this.parentNode = document.getElementById( 'ModalArea' );
this.okay = document.createElement( 'BUTTON' );
this.okay.innerText = 'Okay';
this.parentNode.appendChild( this.okay );
this.cancel = document.createElement( 'BUTTON' );
this.cancel.innerText = 'Cancel';
this.parentNode.appendChild( this.cancel );
this.cancelThrow = document.createElement( 'BUTTON' );
this.cancelThrow.innerText = 'Cancel w/Throw';
this.parentNode.appendChild( this.cancelThrow );
}
async show() {
this.parentNode.classList.add( 'show' );
let modalPromise = new Promise( (resolve, reject) => {
this.okay.onclick = (event) => {
resolve( 'Okay' );
};
this.cancel.onclick = ( event ) => {
reject( 'Cancel' );
};
this.cancelThrow.onclick = ( event ) => {
try {
throw new Error( 'Test of throwing an error!' );
} catch ( err ) {
console.log( 'Event caught error' );
reject( err );
}
};
});
modalPromise.catch( e => {
console.log( 'Promise catch fired!' );
} );
// Clear out the 'modal' buttons after the promise completes.
modalPromise.finally( () => {
this.parentNode.innerHTML = '';
});
return modalPromise;
}
}
async function openModal() {
// Create new modal buttons...
let modal = new Modal();
document.getElementById( 'Result' ).innerHTML = 'Result: ';
// Show the buttons, but wait for the promise to resolve...
try {
document.getElementById( 'Result' ).innerText += await modal.show();
} catch( err ) {
document.getElementById( 'Result' ).innerText += err;
}
// Now that the promise resolved, append more text to the result.
document.getElementById( 'Result' ).innerText += ' Done!';
}
</script>
</head><body>
<button onclick='openModal()'>Open Modal</button>
<div id='ModalArea'></div>
<div id='Result'></div>
</body></html>
Что-то все еще кажется отключенным. После добавления обещания catch
при выборе Cancel w/Throw
ошибка распространяется через modalPromise.catch
, но консоль по-прежнему регистрирует следующую ошибку:
Uncaught (in promise) Error: Test of throwing an error!
at HTMLButtonElement.cancelThrow.onclick