Методы действий (async/await)
Урок: Методы действий (async/await)
Введение
На прошлом уроке мы научились описывать страницы как классы (Page Object) и выносить действия пользователя в методы.
Но если ты внимательно посмотришь на код Playwright, то заметишь одну важную деталь:
const page = {
click: async (selector) => console.log('click', selector),
};
(async () => {
await page.click('#submit');
})();
Почему здесь есть await?
Почему нельзя просто вызвать page.click() и сразу идти дальше?
Дело в том, что взаимодействие с браузером — это не мгновенное действие:
- элемент может появиться не сразу;
- страница может загружаться;
- запросы могут выполняться с задержкой.
Поэтому в Playwright почти все операции возвращают Promise, и их нужно дождаться через await (или .then).
Что значит «асинхронность» простыми словами
Если бы код не ждал завершения действия — он пошёл бы дальше, а UI ещё не обновился. В тестах это ломает проверки.
Что такое async и await
async
Ключевое слово async делает функцию асинхронной (она возвращает Promise).
async function example() {
console.log('example');
}
example().then(() => console.log('готово'));
В классе метод тоже может быть async:
class Demo {
async fillLogin(login) {
console.log('fill', login);
}
}
(async () => {
const d = new Demo();
await d.fillLogin('user');
})();
await
await говорит: «подожди, пока Promise справа завершится».
const page = {
click: async (selector) => console.log('click', selector),
};
(async () => {
await page.click('#submit');
})();
Почему это важно в Playwright
Все основные методы Playwright — асинхронные (goto, fill, click…).
Любой метод, который их вызывает, обычно тоже объявляют async и внутри используют await.
Обновляем наш Page Object
Старый вариант без await внутри (возвращает Promise наружу):
const page = {
fill: async (s, v) => console.log('fill', s, v),
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.loginInput = '#login';
this.passwordInput = '#password';
this.submitButton = '#submit';
}
fillLogin(login) {
return this.page.fill(this.loginInput, login);
}
}
(async () => {
const lp = new LoginPage(page);
await lp.fillLogin('user');
})();
Перепишем «как принято» — async-методы и await внутри:
const page = {
fill: async (s, v) => console.log('fill', s, v),
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.loginInput = '#login';
this.passwordInput = '#password';
this.submitButton = '#submit';
}
async fillLogin(login) {
await this.page.fill(this.loginInput, login);
}
async fillPassword(password) {
await this.page.fill(this.passwordInput, password);
}
async clickSubmit() {
await this.page.click(this.submitButton);
}
}
(async () => {
const loginPage = new LoginPage(page);
await loginPage.fillLogin('user');
await loginPage.fillPassword('123');
await loginPage.clickSubmit();
})();
Что изменилось:
- добавили
asyncк методам; - внутри используем
await; - методы гарантируют, что шаг завершился, прежде чем вернуть управление.
Что будет, если забыть await
Вызов async-функции без await даёт Promise, а не результат:
const page = {
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.submitButton = '#submit';
}
async clickSubmit() {
await this.page.click(this.submitButton);
}
}
(async () => {
const loginPage = new LoginPage(page);
const p = loginPage.clickSubmit();
console.log('это Promise?', p instanceof Promise);
await p;
})();
Правильно дождаться:
const page = {
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.submitButton = '#submit';
}
async clickSubmit() {
await this.page.click(this.submitButton);
}
}
(async () => {
const loginPage = new LoginPage(page);
await loginPage.clickSubmit();
})();
Асинхронный метод более высокого уровня
const page = {
fill: async (s, v) => console.log('fill', s, v),
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.loginInput = '#login';
this.passwordInput = '#password';
this.submitButton = '#submit';
}
async login(login, password) {
await this.page.fill(this.loginInput, login);
await this.page.fill(this.passwordInput, password);
await this.page.click(this.submitButton);
}
}
(async () => {
const loginPage = new LoginPage(page);
await loginPage.login('user', '123');
})();
Как это связано с this
Методы по-прежнему используют this.page и поля селекторов:
const page = {
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.submitButton = '#submit';
}
async clickSubmit() {
await this.page.click(this.submitButton);
}
}
(async () => {
const lp = new LoginPage(page);
await lp.clickSubmit();
})();
Типичная структура Page Object с async/await
const page = {
fill: async (s, v) => console.log('fill', s, v),
click: async (s) => console.log('click', s),
};
class LoginPage {
constructor(page) {
this.page = page;
this.loginInput = '#login';
this.passwordInput = '#password';
this.submitButton = '#submit';
}
async fillLogin(login) {
await this.page.fill(this.loginInput, login);
}
async fillPassword(password) {
await this.page.fill(this.passwordInput, password);
}
async clickSubmit() {
await this.page.click(this.submitButton);
}
async login(login, password) {
await this.fillLogin(login);
await this.fillPassword(password);
await this.clickSubmit();
}
}
(async () => {
const loginPage = new LoginPage(page);
await loginPage.login('user', '123');
})();
Связь с предыдущими уроками
- Урок 1: класс — структура
- Урок 2: методы — поведение
- Урок 3: класс = страница (Page Object)
- Урок 4 (сейчас): методы = асинхронные действия с
await
Итоговое понимание
Асинхронность — основа работы с браузером.
Все действия в Playwright происходят не мгновенно, поэтому методы класса должны быть асинхронными и использовать await.
async/await позволяет:
- выполнять действия последовательно;
- дожидаться результата;
- писать стабильные тесты.
В следующем уроке мы соберём всё вместе и начнём писать полноценные тесты с использованием этих классов.