等待 Dart 异步函数完成

Posted

技术标签:

【中文标题】等待 Dart 异步函数完成【英文标题】:Waiting for a Dart async function to complete 【发布时间】:2019-11-29 20:09:51 【问题描述】:

我在业余时间尝试在 android 上使用 Flutter,我正在努力让一个玩具 Mandelbrot 应用程序等待异步函数完成。

我已经设计了我的 Widget 构建方法来显示一个循环进度指示器,直到计算出 Mandelbrot 点(这需要几秒钟) - 它检查一个布尔标志“_gotPointValues”并在它为假时显示进度指示器。

问题在于异步/等待机制不会等待长时间运行的计算完成。

从行执行:

print("** 从异步未来获取数据 **");

.. 进入 MandelbrotWidgetState._getMandelbrotPoints 函数后立即继续。

我确定我遗漏了一些非常明显的东西 - 如果有人能发现我做错了什么,感谢任何帮助。

谢谢。

import 'package:flutter/material.dart';
import 'package:phil_flutter_app/mandelbrot_painter.dart';
import 'package:phil_flutter_app/mandelbrot_points.dart';

/// This class defines a Mandelbrot widget.
class MandelbrotWidget extends StatefulWidget

    @override
    MandelbrotWidgetState createState()
    
        return MandelbrotWidgetState();
    


/// This class defines a Mandelbrot widget state.
class MandelbrotWidgetState extends State<MandelbrotWidget>

    int _pointsWidth;

    int _pointsHeight;

    int _maxIterations;

    MandelbrotPoints _mandelbrotPoints;

    MandelbrotPainter _mandelbrotPainter;

    List _pointValues;

    bool _gotPointValues;

    /// Initialise.
    void initState()
    
        super.initState();

        print("initState");

        _pointsWidth = 1080;

        _pointsHeight = 1920;

        _maxIterations = 256;

        _mandelbrotPoints = new MandelbrotPoints(_pointsWidth, _pointsHeight, _maxIterations);

        _mandelbrotPainter = new MandelbrotPainter(_pointsWidth, _pointsHeight);

        _pointValues = null;

        _gotPointValues = false;

        _getMandelbrotPoints();
    

    /// Get the mandelbrot points.
    void _getMandelbrotPoints() async
    
        _pointValues = await _mandelbrotPoints.calculateMandelbrotPoints();

        print("** Got data from async future **");

        setState(() 
            _gotPointValues = true;
        );
    

    @override
    Widget build(BuildContext context)
    
        print("build");

        _mandelbrotPainter.setDevicePixelRatio(MediaQuery.of(context).devicePixelRatio);

        return Scaffold(
            backgroundColor: Colors.black,
            appBar: AppBar(
                title: Text("PH - Mandelbrot Test - Google Flutter"),
                centerTitle: true
            ),
            body: _buildBody(),
            floatingActionButton: new FloatingActionButton(
                onPressed: _redraw,
                tooltip: 'Refresh',
                child: Icon(Icons.refresh),
            ),
            floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat
        );
    

    /// Build the body.
    Widget _buildBody()
    
        if (_gotPointValues)
        
            print("** Building fractal **");

            _mandelbrotPainter.setPointValues(_pointValues);

            return new Center(
                child: new CustomPaint(
                    painter: _mandelbrotPainter,
                    child: Center()
                )
            );
        
        else
        
            print("** Building progress indicator **");

            return new Center(
                child:  new CircularProgressIndicator()
            );
        
    

    /// Redraw.
    void _redraw()
    
        _mandelbrotPainter.repaint();
    



/// This class calculates points for the Mandelbrot fractal.
class MandelbrotPoints

    int width;

    int height;

    int _maxIterations;

    /// Constructor.
    MandelbrotPoints(int width, int height, int _maxIterations)
    
        this.width = width;
        this.height = height;
        this._maxIterations = _maxIterations;
    

    /// Calculate the Mandelbrot points.
    Future<List> calculateMandelbrotPoints() async
    
        print("MandelbrotPoints.calculateMandelbrotPoints");

        int w = width;
        int h = height;

        List _pointValues = new List.generate(w, (i)
        => new List(h));

        int xOffset = 0;
        int yOffset = -(h ~/ 6);

        double zoom = 0.8;

        for (int x = 0; x < w; x ++)
        
            for (int y = 0; y < h; y ++)
            
                double cRe = 1.5 * (y + yOffset - h / 2) / (0.5 * zoom * h);
                double cIm = 1.0 * (x + xOffset - w / 2) / (0.5 * zoom * w);

                double zx = 0;
                double zy = 0;

                int iteration = 0;

                while (zx * zx + zy * zy <= 4 && iteration < _maxIterations)
                
                    double zyNew = zy * zy - zx * zx + cRe;

                    zx = 2 * zx * zy + cIm;

                    zy = zyNew;

                    iteration ++;
                

                if (iteration < _maxIterations)
                
                    _pointValues[x][y] = iteration;
                
                else
                
                    _pointValues[x][y] = -1;
                
            
        

        return _pointValues;
    



import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:phil_flutter_app/mandelbrot_colour_utils.dart';

/// This class defines a custom painter
/// to draw a Mandelbrot fractal.
class MandelbrotPainter extends CustomPainter

    int _pointsWidth;

    int _pointsHeight;

    List _pointValues;

    Random _random;

    MandelbrotColorUtils _mandelbrotColorUtils;

    int _randomHue;

    Size _size;

    Canvas _canvas;

    double _devicePixelRatio;

    Paint _paint;

    double _width;

    double _height;

    /// Constructor.
    MandelbrotPainter(int _pointsWidth, int _pointsHeight)
    
        print("MandelbrotPainter constructor");

        this._pointsWidth = _pointsWidth;

        this._pointsHeight = _pointsHeight;

        _random = new Random();

        _mandelbrotColorUtils = new MandelbrotColorUtils();

        _randomHue = _getRandomNumber(0, 255);

        _canvas = null;

        _width = -1;

        _height = -1;

        _paint = null;
    

    /// Set the device pixel ratio.
    void setDevicePixelRatio(double devicePixelRatio)
    
        _devicePixelRatio = devicePixelRatio;
    

    @override
    void paint(Canvas canvas, Size size)
    
        print("paint");

        _size = size;

        _width = size.width;

        _height = (size.height * 0.86);

        _canvas = canvas;

        _paint = Paint();
        _paint.strokeWidth = 1;
        _paint.style = PaintingStyle.fill;

        _randomHue = _getRandomNumber(0, 255);

        _drawMandelbrotPoints();
    

    /// Repaint.
    void repaint()
    
        paint(_canvas, _size);
    

    /// Set the point values.
    void setPointValues(List _pointValues)
    
        this._pointValues = _pointValues;
    

    /// Draw the Mandelbrot points.
    void _drawMandelbrotPoints()
    
        print("_drawMandelbrotPoints");

        _paint.color = Colors.black;
        _canvas.drawRect(Rect.fromLTWH(0, 0, _width, _height), _paint);

        int w = (_width * _devicePixelRatio).round();
        int h = (_height * _devicePixelRatio).round();

        for (int x = 0; x < _pointsWidth; x ++)
        
            for (int y = 0; y < _pointsHeight; y ++)
            
                int pixelValue = _pointValues[x][y];

                if (pixelValue == -1)
                
                    _paint.color = Color.fromARGB(255, 0, 0, 0);
                
                else
                
                    double h = ((2 * pixelValue) + _randomHue).toDouble();

                    _paint.color =
                        _mandelbrotColorUtils.createColorFromHsv(h, 100, 100);
                

                List<Offset> points = new List();

                int pointX = (x * (w / _pointsWidth)).round();
                int pointY = (y * (h / _pointsHeight)).round();

                points.add(Offset(pointX.toDouble() / _devicePixelRatio,
                    pointY.toDouble() / _devicePixelRatio));

                _canvas.drawPoints(PointMode.points, points, _paint);
            
        
    

    @override
    bool shouldRepaint(CustomPainter oldDelegate)
    
        return true;
    

    /// Get a random number.
    int _getRandomNumber(int min, int max)
    
        return min + _random.nextInt(max - min);
    


【问题讨论】:

在异步调用中使用 AWAIT 请贴出整个代码。帮助您更容易。 谢谢,编辑原始帖子以包含所有代码。 【参考方案1】:

即使calculateMandelbrotPoints() 是一个异步函数,并且进行了大量计算,但这并不意味着它会花费太长时间来执行,在这种情况下它不会。它执行速度很快,您可以立即获得结果。

我写了两个函数给你演示一下,你可以在这个dart pad玩玩

【讨论】:

谢谢,你是对的,我在那里猜错了。实际上,画家类中的 _drawMandelbrotPoints() 占用了大部分时间。我正在重写整个东西,感谢飞镖垫的例子,它应该对异步的东西有用。【参考方案2】:

尝试更改 void _getMandelbrotPoints() 异步 到 Future _getMandelbrotPoints() 异步

【讨论】:

以上是关于等待 Dart 异步函数完成的主要内容,如果未能解决你的问题,请参考以下文章

Dart - 在 for 循环中等待所有异步任务

异步/等待/然后在 Dart/Flutter 中

理解 Dart 中的异步和同步

等待我的班级初始化(或如何等待 Future 完成)?

在映射下一项之前,异步等待映射不等待异步函数在映射函数内部完成

Dart语法基础补充