Allow async constructors (or at least factory constructors)

This issue was originally filed by Jim.J.Si…@gmail.com


What steps will clearly show the issue / need for enhancement?

  1. Attempt to create a constructor and mark the body as async

What is the current output?
A warning from the analyzer stating that constructors cannot be marked async

What would you like to see instead?
The ability to mark a constructor async. Or at the very least, allow factory constructors to be marked as async. I ran into a situation where I needed to do some post construction logic using an internal method on the constructed object. The method I wanted to use is an asynchronous method and cannot be changed. I ended up using a “static factory method” along the lines of Class.getInstance(), but it seems like this is a situation that would be ideal for a factory constructor.

What version of the product are you using? On what operating system?
1.9.1 on OSX

Please provide any additional information below.

Example:
class Foo {

  //Not allowed 🙁
  Foo() async {
    await _bar();
  }

  Foo._internal() {
    //normal construction logic
  }
  
  //Also not allowed 🙁
  factory Foo.factory() async {
    Foo foo = new Foo._internal();
    await foo._bar();
    return foo;
  }

  //Works! But not ideal 🙁
  static Future<Foo> getInstance() async {
    Foo foo = new Foo._internal();
    await foo._bar();
    return foo;
  }

  Future _bar() async {
    //do some expensive work asynchronously
  }

}

Author: Fantashit

9 thoughts on “Allow async constructors (or at least factory constructors)

  1. This comment was originally written by Jim.J.Simon…@gmail.com


    Simple case: I have an object with expensive logic for creating its initial state. It could be file I/O, network requests, data crunching, etc. I don’t want the user to use the object until that expensive logic if finished, and I don’t want to block while it’s being executed.

    class Foo {

      var expensiveResult;

      //Also not allowed 🙁
      factory Foo() async {
        Foo foo = new Foo._internal();
        await foo._expensive();
        return foo;
      }

      Foo._internal() {
        //normal construction logic
      }

      Future _expensive() async {
        //do some expensive work asynchronously
        expensiveResult = …
      }
    }

    main() {
      Foo foo = await new Foo(); //foo shouldn’t be available until its expensive setup is complete
      expect(foo.expensiveResult, isNotNull);
    }

  2. This comment was originally written by @kaendfinger


    Why not just assign a completer in the constructor, and ask the user to do something like instance.onReady

    class MyClass {
      Future onReady;

      MyClass() {
        onReady = new Future(() {
          // Do Stuff
        });
      }
    }

    main() async {
      var instance = new MyClass();
      await instance.onReady;
      print(“Ready”);
    }

    Imho, you should never do anything expensive in a constructor. Constructing objects should generally be fast. However, for factory constructors, I do see the benefit. What if you load objects from a database? What then do you do with factory constructors?

  3. I try to implements Future interface, then factory could return a Future and able to be await.

    import 'dart:async';
    import 'dart:mirrors';
    
    @proxy
    class AsyncFact implements Future {
      factory AsyncFact() {
        return new AsyncFact._internal(new Future.delayed(
            const Duration(seconds: 1), () => '[Expensive Instance]'));
      }
    
      AsyncFact._internal(o) : _mirror = reflect(o);
    
      final InstanceMirror _mirror;
      @override
      noSuchMethod(Invocation invocation) => _mirror.delegate(invocation);
    }
    
    main() async {
      print(await new AsyncFact());
    }
  4. @Ticore: I’m not sure what you are asking for here. Your code already returns a future (an AsyncFact even) which you can “await” on.

    You can’t mark your factory constructor async because even if it worked, it would return a system Future, not an AsyncFact, so it’s not a valid return value for a constructor on AsyncFact.

  5. +1 for async factory constructors. Was my naive approach for constructing a database-backed object for a web-app; didn’t work, so everything since then on it has been ‘work-around’ time for me. Perhaps ideological considerations out-weigh this, but there is justification on the ‘batteries included’ side of things.

  6. +1 for async support in factory constructors in order to allow construction of objects with database data and avoid the need to call the async method for complete the object.

  7. Now that new is optional, it is almost identical to have a factory constructor static method:

    class A {
      factory A.a() => ...
    }
    
    class B {
      static B b() => ...
    }
    
    void main() {
      A.a();
      B.b();
    }

    … and of course, static methods can have a return type of Future:

    import 'dart:async';
    
    class C {
      static Future<C> load(String url) => ... 
    }

    @lrhn Unless you see this happening (re-open in that case), let’s close this.

  8. @matanlurey That’s true, with a small caveat: the default constructor name (the best name, sadly) can’t be used for static methods. This doesn’t work:

    import 'dart:async';
    
    class C {
      static Future<C>(String url) => ... 
    }
    
    var c = await C('http://google.com');
  9. Is it really going to end here?

    I’m amazed that not a lot of people are encouraging this :/

    I’ve come across quite a few moments now where it really could have made everything smoother, like a Settings class with data from your “dynamic” json file for example.

Comments are closed.