Event Loop — Flutter

Tempo de leitura: 5 minutes

 

O Dart usa um modelo de programação orientado a eventos e você costuma trabalhar com loops de eventos para lidar com operações assíncronas de forma eficiente. O Dart fornece a classe EventLoop como parte da biblioteca dart:async para gerenciar eventos e tarefas assíncronas. O loop de eventos é crucial para o tratamento de eventos, como entrada do usuário, solicitações de rede e temporizadores, sem bloquear o thread principal do aplicativo.

O loop de eventos é um processo invisível que gerencia a execução de eventos de código e retornos de chamada. É uma parte crucial do tempo de execução do Dart, garantindo que todas as chamadas em seu código Dart sejam executadas na ordem correta. O loop de eventos do Dart funciona verificando continuamente duas filas:

A fila de eventos e a fila de microtarefas.

A função do loop de eventos é lidar com eventos como eventos de mouse, eventos de toque ou outros eventos externos, como dados de um servidor. Esses eventos são adicionados à fila de eventos.

A fila de microtarefas é usada para ações internas assíncronas curtas provenientes do seu código Dart.

 

Função do Event Loop no Dart

O Dart é uma linguagem de thread único. O loop de eventos desempenha uma função essencial no gerenciamento da execução do código Dart. Quando você inicia o aplicativo Dart, o isolado principal (um novo isolado) é criado e o event loop é iniciado. A função principal, geralmente void main(), é a primeira a ser executada de forma síncrona.

A programação assíncrona no Dart envolve o uso de objetos Future e funções assíncronas. Sempre que escrevemos código assíncrono, programamos tarefas para serem executadas posteriormente. Por exemplo, quando usamos await Future, estamos dizendo ao loop de eventos do Dart para concluir outro código Dart primeiro e voltar quando o Future estiver concluído.

O event loop do Dart considera primeiro a fila de microtarefas. Se a fila de microtarefas estiver vazia, ele passará para o próximo evento na fila de eventos. Esse mecanismo garante que o código síncrono e as ações internas assíncronas curtas sejam priorizados em relação ao tratamento de eventos como entrada do usuário ou dados de um servidor.

 

Como funciona o Event Loop do Dart

O mecanismo event loop do Dart é um loop infinito que mantém seu aplicativo Dart em execução. Ele começa quando seu aplicativo é iniciado e continua até que não haja mais eventos para processar. O loop de eventos do Dart funciona verificando duas filas: a fila de eventos e a fila de microtarefas.

Aqui está uma visão simplificada de como o event loop do Dart funciona:

void main() {
    print('Dart app starts');
    Future(() => print('This is a new Future'));
    scheduleMicrotask(() => print('This is a micro task'));
    print('Dart app ends');
  }

 

Microtask Queue

A microtask queue é uma parte do mecanismo de loop de eventos do Dart que lida com ações internas assíncronas curtas. Essas ações geralmente são o resultado de chamadas scheduleMicrotask em seu código Dart. O microtask queue tem prioridade sobre a fila de eventos no loop de eventos do Dart. Isso significa que todas as tarefas na fila de microtarefas serão processadas antes que o loop de eventos considere a fila de eventos.

O microtask queue é ideal para tarefas que você deseja que sejam concluídas o mais rápido possível, mas não imediatamente. Por exemplo, você pode usar o microtask queue para atrasar algum cálculo ou para permitir que a interface do usuário seja atualizada entre as tarefas.

void main() {
     print('Dart app starts');
     scheduleMicrotask(() => print('Microtask 1'));
     scheduleMicrotask(() => print('Microtask 2'));
     Future(() => print('This is a new Future'));
     print('Dart app ends');
   }

Nesse código de exemplo, a Microtask 1 e a Microtask 2 são processadas antes do novo Future, embora o Future tenha sido adicionado ao microtask queue antes de a Microtask 2 ter sido adicionada ao microtask queue. Isso demonstra como o Dart prioriza as microtarefas em relação aos eventos.

 

Event Queue

A event queue é outro componente essencial do loop de eventos do Dart. Ela é responsável pelo tratamento de eventos, como interações do usuário (como eventos de mouse ou eventos de toque), temporizadores e operações de E/S. Quando você cria um novo Future ou quando ocorre um evento externo, um evento é adicionado à fila de eventos.

A fila de eventos é processada após a fila de microtarefas. Isso significa que todas as tarefas na fila de microtarefas são concluídas antes que o loop de eventos do Dart comece a processar a fila de eventos.

Aqui está um exemplo simples de adição de um evento à fila de eventos:

void main() {
    print('Dart app starts');
    Future(() => print('This is a new Future'));
    print('Dart app ends');
  }

o novo Future é adicionado à fila de eventos e é processado após a conclusão de todas as microtarefas.

 

Diferenças entre Microtask e Event Queue

A principal diferença entre as filas de microtarefas e de eventos está em sua prioridade no loop de eventos do Dart. A fila de microtarefas tem prioridade sobre a fila de eventos. Isso significa que todas as tarefas na fila de microtarefas são processadas antes que o loop de eventos do Dart comece a processar a fila de eventos.

Vamos ver alguns exemplos.

Na programação assíncrona, o loop de eventos do Dart desempenha um papel crucial no gerenciamento da execução do código Dart. Quando você usa objetos Future ou funções assíncronas, está agendando tarefas para serem executadas posteriormente. Essas tarefas são adicionadas à fila de eventos e são processadas quando o loop de eventos do Dart chega até elas.

Vamos considerar um cenário do mundo real em que o loop de eventos é crucial para lidar com operações assíncronas em um aplicativo Flutter: lidar com a autenticação do usuário e buscar dados do usuário em um servidor.

Autenticação de usuário: Imagine que você tem um aplicativo Flutter que requer autenticação de usuário. Quando o usuário insere suas credenciais e clica no botão “Login”, o aplicativo precisa se comunicar com um servidor para verificar as credenciais.

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: LoginPage(),
    );
  }
}

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  bool isLoading = false;

  void _login() async {
    setState(() {
      isLoading = true;
    });

    // Simulate a network request for authentication
    await Future.delayed(Duration(seconds: 2));

    // After authentication, fetch user data
    _fetchUserData();
  }

  void _fetchUserData() async {
    // Simulate another network request to fetch user data
    await Future.delayed(Duration(seconds: 2));

    // Navigate to the home screen with user data
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (context) => HomeScreen(userData: "User123")),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Login Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: isLoading ? null : _login,
              child: Text('Login'),
            ),
            if (isLoading) CircularProgressIndicator(),
          ],
        ),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final String userData;

  HomeScreen({required this.userData});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: Text('Welcome, $userData!'),
      ),
    );
  }
}

Nos exemplos acima. O método _login simula uma solicitação de rede para autenticação do usuário. Enquanto aguarda a conclusão da autenticação, a interface do usuário permanece responsiva graças ao loop de eventos e à natureza assíncrona do Dart.

  • Depois que a autenticação é bem-sucedida, o método _fetchUserData simula outra solicitação de rede para buscar dados adicionais do usuário. Novamente, essa operação é tratada de forma assíncrona sem bloquear o thread principal.
  • O aplicativo navega para a tela inicial(HomeScreen) depois que a autenticação e a busca de dados são concluídas.

Em um cenário do mundo real, as solicitações de rede podem envolver APIs de autenticação reais e pontos de extremidade de dados. Ao aproveitar os recursos assíncronos do Dart e o loop de eventos, os aplicativos Flutter podem proporcionar uma experiência de usuário tranquila, mesmo quando lidam com operações potencialmente demoradas, como autenticação de usuário e obtenção de dados. O aplicativo permanece responsivo, e os usuários podem interagir com a interface do usuário enquanto o aplicativo se comunica com o servidor em segundo plano.

Bata palmas 👏 Se este artigo ajudar você.