4
votes

callbacks or asynchronous methods or other options

A solution to the callback plague is "await" and "async" or more specifacally 'dart:async' library.

Now, what is the cost of asynchrony?
When should we not use them?
What are the other alternatives?

The below is a badly coded non-polymer custom element that acts like a messageBox in desktop environment. It gives me less braces and parenthesis-es but requires the caller to be also async or use "show().then((v){print(v);});" pattern.
Should I avoid the pattern like this?
Is callback better? Or there is an even smarter way?

Polling version

import 'dart:html';
import 'dart:async';
void init(){
  document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
  ListModal.created():super.created();
  String _modal_returns="";
  void set modal_returns(String v){
    ///use the modal_returns setter to
    ///implement a custom behaviour for
    ///the return value of the show method 
    ///within the callback you can pass on calling append .
    _modal_returns=v;
  }

  factory ListModal(){
    var e = new Element.tag('list-modal');
    e.style..backgroundColor="olive"
      ..position="absolute"
      ..margin="auto"
      ..top="50%"
      ..verticalAlign="middle";
    var close_b = new DivElement();
    close_b.text = "X";
    close_b.style..right="0"
      ..top="0"
      ..margin="0"
      ..verticalAlign="none"
      ..backgroundColor="blue"
      ..position="absolute";
    close_b.onClick.listen((_){
      e.hide();
    });
    e.append(close_b,(_)=>e.hide());
    e.hide();
    return e;
  }
  @override
  ListModal append(
      HtmlElement e,
      [Function clickHandler=null]
      ){
    super.append(e);
    if(clickHandler!=null) {
      e.onClick.listen(clickHandler);
    }else{
      e.onClick.listen((_){
        this.hide();
        _modal_returns = e.text;
      });
    }
    return this;
  }
  Future<String> show() async{
    _modal_returns = '';
    this.hidden=false;
    await wait_for_input();
    print(_modal_returns);
    return _modal_returns;
  }
  wait_for_input() async{
    while(_modal_returns=="" && !this.hidden){
      await delay();
    }
  }
  void hide(){
    this.hidden=true;
  }
  Future delay() async{
    return new Future.delayed(
        new Duration(milliseconds: 100));
  }
}

Non-polling version

In response to Günter Zöchbauer's wisdom(avoid polling), posting a version that uses a completer. Thanks you as always Günter Zöchbauer:

import 'dart:html';
import 'dart:async';
void init(){
  document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
  ListModal.created():super.created();
  String _modal_returns="";
  Completer _completer;
  void set modal_returns(String v){
    ///use the modal_returns setter to
    ///implement a custom behaviour for
    ///the return value of the show method.
    ///Use this setter within the callback for
    ///append. Always call hide() after
    ///setting modal_returns.
    _modal_returns=v;
  }

  factory ListModal(){
    var e = new Element.tag('list-modal');
    e.style..backgroundColor="olive"
      ..position="absolute"
      ..margin="auto"
      ..top="50%"
      ..verticalAlign="middle";
    var close_b = new DivElement();
    close_b.text = "X";
    close_b.style..right="0"
      ..top="0"
      ..margin="0"
      ..verticalAlign="none"
      ..backgroundColor="blue"
      ..position="absolute";
    close_b.onClick.listen((_){
      e.hide();
    });
    e.append(close_b,(_){e.hide();});
    e.hide();
    return e;
  }
  @override
  ListModal append(
      HtmlElement e,
      [Function clickHandler=null]
      ){
    super.append(e);
    if(clickHandler!=null) {
      e.onClick.listen(clickHandler);
    }else{
      e.onClick.listen((_){
        _modal_returns = e.text;
        this.hide();
      });
    }
    return this;
  }
  Future<String> show() async{
    _modal_returns = '';
    _completer = new Completer();
    this.hidden=false;
    return _completer.future;
  }
  void hide(){
    hidden=true;
    _completer?.complete(_modal_returns);
    _completer=null;
  }
}
1

1 Answers

3
votes

Usually there is no question whether async should be used or not. Usually one would try to avoid it. As soon as you call an async API your code goes async without a possibility to choose if you want that or not. There are situations where async execution is intentionally made async. For example to split up large computation in smaller chunks to not starve the event queue from being processed.

On the server side there are several API functions that allow to choose between sync and async versions. There was an extensive discussion about when to use which. I'll look it up and add the link.

The disadvantages of using async / await instead of .then() should be minimal.

  • minimal Dart SDK version with async / await support is 1.9.1
  • the VM needs to do some additional rewriting before the code is executed the first time, but this is usually neglectable.

Your code seems to do polling.

wait_for_input() async {
  while(_modal_returns=="" && !this.hidden){
    await delay();
  }
}

This should be avoided if possible. It would be better to let the modal manage its hidden state itself (by adding a hide() method for example), then it doesn't have to poll whether it was hidden from the outside.