Flutter Series Four-Introduction to Dart Language

Important concept

When learning the Dart language, please remember the following facts and concepts:

  • Everything that can be placed in a variable is an object , and every object is an instance of a class . Both numbers, functions and null are objects. All objects inherit from the [Object] class.
  • Although Dart is strongly typed, the type declaration is optional because Dart can infer the type. If you want to make it clear that no type is required, use [special type dynamic].
  • Dart supports common types, such as List<int> (list of integers) or List<dynamic> (list of objects of any type).
  • Dart supports top-level functions (such as main()) and functions bound to classes or objects (static and instance methods, respectively). You can also create functions in functions (nested or local functions).
  • Similarly, Dart supports top-level variables, as well as variables bound to classes or objects (static and instance variables). Instance variables are sometimes called fields or attributes.
  • Unlike Java, Dart does not have public, protected, and private keywords. If the identifier starts with an underscore (_), the identifier is private to its library. For details, see [Library and Visibility].
  • Identifiers can start with a letter or underscore (_), and then any combination of these characters and numbers.
  • Sometimes, whether something is an expression or a statement is very important, so these two words must be accurate.
  • Dart tools can report two kinds of problems: warnings and errors. Warnings simply indicate that your code may not work, but they will not prevent your program from executing. The error can be a compile-time error or a runtime error. Compile-time errors prevent the execution of the code; runtime errors cause exceptions when the code is executed.

The design goal of Dart should be to benchmark both Java and JavaScript. Dart is very similar to Java in terms of static syntax, such as type definitions, function declarations, generics, etc., and is very similar to JavaScript in terms of dynamic features, such as functions. Style features, asynchronous support, etc. In addition to integrating the strengths of Java and JavaScript languages, Dart also has some other expressive syntax, such as optional named parameters, ..(cascade operator) and ?.(conditional member access operator) and ??(empty assignment operator) ). In fact, readers who know more about programming languages ​​will find that Dart actually sees not only the shadows of Java and JavaScript, but also other programming languages. For example, named parameters have long been used in Objective-C and Swift. It is very common, and the ??operator already exists in the Php 7.0 syntax, so we can see that Google has high hopes for the Dart language, and it wants to build Dart into a programming language with the best of hundreds of families.

Next, we first give a brief introduction to Dart syntax, and then make a brief comparison between Dart and JavaScript and Java to facilitate readers' better understanding.

Note: This article will mainly introduce the grammatical features commonly used in Flutter development. If you want to learn more about Dart, readers can go to the Dart official website to learn. Now there are a lot of Dart related materials on the Internet. In addition, Dart 2.0 has been officially released, and all examples here use Dart 2.0 syntax.

Variable declaration

  1. where

    Similar to JavaScript var, it can receive any type of variable, but the biggest difference is that once the var variable in Dart is assigned, the type will be determined, and you cannot change its type, such as:

    var t;
    t="hi world";
    // 下面代码在dart中会报错,因为变量t的类型已经确定为String,
    // 类型一旦确定后则不能再更改其类型。
    t=1000;
    

    The above code is no problem in JavaScript. Front-end developers need to pay attention. The reason for this difference is that Dart itself is a strongly typed language. Any variable has a certain type. In Dart, when you vardeclare a variable Later, Dart will infer its type based on the type of the first assignment data when compiling, and its type has been determined after the completion of the compilation, and JavaScript is a purely weakly typed scripting language, and var is just a way of declaring variables.

  2. dynamicObject

    ObjectIt is the root base class of all objects in dart, which means that all types are Objectsubclasses (including Function and Null), so any type of data can be assigned to the Objectdeclared object. dynamicSame varas keywords, and declared variables can be assigned any object. and dynamicthe Objectsimilarities that they declare a variable of the type of assignment can be changed at a later stage.

     dynamic t;
     Object x;
     t = "hi world";
     x = 'Hello Object';
     //下面代码没有问题
     t = 1000;
     x = 1000;
    

    dynamicThe Objectdifference is that the dynamicdeclared object compiler will provide all possible combinations, and the Objectdeclared object can only use the properties and methods of Object, otherwise the compiler will report an error. For example:

     dynamic a;
     Object b;
     main() {
         a = "";
         b = "";
         printLengths();
     }   
    
     printLengths() {
         // no warning
         print(a.length);
         // warning:
         // The getter 'length' is not defined for the class 'Object'
         print(b.length);
     }
    

    Not being given a variable, the variable b compiler will complain dynamicof this feature and Objective-Cthe idrole like. dynamicThis feature allows us to use it need extra attention, it is very easy to introduce a runtime error.

  1. final and const

    If you never intend to change a variable, then use finalor constnot var, nor is it a type. A finalvariable can be set only once, that the difference between the two: constthe variable is a compile-time constant, finalvariable used when first initialized. For variables that are modified finalor constmodified, the variable type can be omitted, such as:

    //可以省略String这个类型声明
    final str = "hi world";
    //final String str = "hi world"; 
    const str1 = "hi world";
    //const String str1 = "hi world";
    

function

Dart is a true object-oriented language, so even functions are objects and have a type Function . This means that functions can be assigned to variables or passed as parameters to other functions, which is a typical feature of functional programming.

  1. Function declaration

    bool isNoble(int atomicNumber) {
      return _nobleGases[atomicNumber] != null;
    }
    

    If the dart function declaration does not explicitly declare the return value type, it will be dynamicprocessed by default . Note that the function return value does not have type inference:

    typedef bool CALLBACK();
    
    //不指定返回类型,此时默认为dynamic,不是bool
    isNoble(int atomicNumber) {
      return _nobleGases[atomicNumber] != null;
    }
    
    void test(CALLBACK cb){
       print(cb()); 
    }
    //报错,isNoble不是bool类型
    test(isNoble);
    
  2. For functions that contain only one expression, you can use shorthand syntax

    bool isNoble (int atomicNumber )=> _nobleGases [ atomicNumber ] != null ;
    
  3. Function as a variable

    var say= (str){
      print(str);
    };
    say("hi world");
    
  4. Function passed as a parameter

    void execute(var callback){
        callback();
    }
    execute(()=>print("xxx"))
    
  5. Optional positional parameters

    Wrap a set of function parameters, marked with [] as optional positional parameters:

    String say(String from, String msg, [String device]) {
      var result = '$from says $msg';
      if (device != null) {
        result = '$result with a $device';
      }
      return result;
    }
    

    The following is an example of calling this function without optional parameters:

    say('Bob', 'Howdy'); //结果是: Bob says Howdy
    

    The following is an example of calling this function with the third parameter:

    say('Bob', 'Howdy', 'smoke signal'); //结果是:Bob says Howdy with a smoke signal
    
  6. Optional named parameters

    When defining a function, use {param1, param2, …} to specify named parameters. E.g:

    //设置[bold]和[hidden]标志
    void enableFlags({bool bold, bool hidden}) {
        // ... 
    }
    

    When calling a function, you can use designated named parameters. E.g:paramName: value

    enableFlags(bold: true, hidden: false);
    

    Optional named parameters are used a lot in Flutter.

Asynchronous support

The Dart library has a lot of return Futureor Streamobject functions. These functions are called asynchronous functions : they will only return after setting up some time-consuming operations, such as IO operations. Instead of waiting for this operation to complete.

asyncAnd awaitkeywords support asynchronous programming, and run asynchronous code that you write that is very similar to synchronous code.

Future

FutureIt is Promisevery similar to JavaScript , which represents the final completion (or failure) of an asynchronous operation and its result value. Simply put, it is used to process asynchronous operations. If the asynchronous process succeeds, the successful operation is executed, and if the asynchronous process fails, the error is captured or the subsequent operation is stopped. A Future will only correspond to one result, either success or failure.

Due to its many functions, here we only introduce its commonly used APIs and features. Also, please remember that Futurethe return value of all APIs is still an Futureobject, so it can be easily chained.

Future.then

To facilitate the example, in this example we Future.delayedcreated a delayed task (the actual scenario will be a real time-consuming task, such as a network request), that is, the result string "hi world!" is returned after 2 seconds, and then we in thenreceiving the result of the asynchronous and print the results, as follows:

Future.delayed(new Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});

Future.catchError

If an error occurs in the asynchronous task, we can catchErrorcatch the error in, we change the above example to:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}).catchError((e){
   //执行失败会走到这里  
   print(e);
});

In this example, we throw an exception in asynchronous tasks, the thencallback function will not be executed and replaced by catchErrorthe callback function will be called; however, not only catchErrorcallback to catch the error, thenthe method as well as an optional Parameters onError, we can also use it to catch exceptions:

Future.delayed(new Duration(seconds: 2), () {
    //return "hi world!";
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print(e);
});

Future.whenComplete

Sometimes, we will encounter scenarios where something needs to be done regardless of the success or failure of the asynchronous task execution, such as popping up a loading dialog before the network request, and closing the dialog after the request is over. In this scenario, there are two methods. The first is to close the dialog box in thenor respectively catch. The second is to use Futurethe whenCompletecallback. Let's change the above example:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

Future.wait

Sometimes, we need to wait for multiple asynchronous tasks to complete before performing some operations. For example, if we have an interface, we need to obtain data from two network interfaces. After the acquisition is successful, we need to perform specific operations on the two interface data. What should I do if it is displayed on the UI interface after processing? The answer is Future.waitthat it accepts an Futurearray of parameters. The success callback Futurewill only be triggered after all of the arrays are executed successfully then. As long as one Futureexecution fails, the error callback will be triggered. Next, we use simulation Future.delayedto simulate two asynchronous tasks for data acquisition. When the two asynchronous tasks are executed successfully, the results of the two asynchronous tasks are spliced ​​and printed out. The code is as follows:

Future.wait([
  // 2秒后返回结果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回结果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

After executing the above code, you will see "hello world" in the console after 4 seconds.

Async/await

async/awaitThe async/awaitfunctions and usage of Dart and JavaScript are exactly the same. If you already know async/awaitthe usage of JavaScript , you can skip this section directly.

Callback hell

If there is a lot of asynchronous logic in the code, and a large number of asynchronous tasks depend on the results of other asynchronous tasks, there will inevitably be a Future.thenset of callbacks in the callback. For example, for example, there is a demand scenario where the user first logs in, and after the login is successful, the user ID is obtained, and then the user ID is used to request the user's personal information. After obtaining the user's personal information, we need to use it for convenience. Cached in the local file system, the code is as follows:

//先分别定义各个异步任务
Future<String> login(String userName, String pwd){
    ...
    //用户登录
};
Future<String> getUserInfo(String id){
    ...
    //获取用户信息 
};
Future saveUserInfo(String userInfo){
    ...
    // 保存用户信息 
};

Next, execute the entire task flow:

login("alice","******").then((id){
 //登录成功后通过,id获取用户信息    
 getUserInfo(id).then((userInfo){
    //获取用户信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用户信息,接下来执行其它操作
        ...
    });
  });
})

You can feel that if there are a large number of asynchronous dependencies in the business logic, the above situation will occur in the callback inside the callback. Too much nesting will cause the code readability to decrease and the error rate to increase, and it is very difficult Maintenance, this problem is vividly called callback hell (Callback hell) . The problem of callback hell was very prominent in JavaScript before, and it was also the point where JavaScript was the most complained about. However, with the release of ECMAScript6 and ECMAScript7 standards, this problem has been solved very well, and the two artifacts to solve the callback hell are the introduction of ECMAScript6 Promise, And introduced in ECMAScript7 async/await. In Dart, the two in JavaScript are almost completely translated: Futureequivalent Promise, without async/awaiteven changing the name. Next, let's look at the adoption Futureand async/awaithow to eliminate the nesting problem in the above example.

Use Future to eliminate callback hell

login("alice","******").then((id){
      return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //执行接下来的操作 
}).catchError((e){
  //错误处理  
  print(e);
});

As mentioned above, " FutureThe return value of all APIs is still an Futureobject, so it can be easily chained." If there is one returned Futurein then, it futurewill be executed, and the following will be triggered after execution thenCallbacks, in this order downwards, avoid layers of nesting.

Use async/await to eliminate callback hell

By Futurecallback back Futurethe way though to avoid nested layers, but still with a layer of a callback, is there a way to allow us to write synchronization code can be like manner as to perform asynchronous tasks without using the callback? The answer is yes, it is necessary to use async/await, let's look at the code directly, and then explain, the code is as follows:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}
  • asyncUsed to indicate that the function is asynchronous, the defined function returns an Futureobject, you can use the then method to add a callback function.
  • awaitFollowed by a Futureshowing wait for the asynchronous task is completed, the asynchronous completion will go down; awaitmust appear asyncwithin the function.

As you can see, we have async/awaitrepresented an asynchronous stream with synchronous code.

In fact, whether in JavaScript or Dart, it async/awaitis just a syntactic sugar, and the compiler or interpreter will eventually convert it into a Promise (Future) call chain.

Stream

StreamIt is also used to receive asynchronous event data, and the Futuredifference is that it can receive the results (success or failure) of multiple asynchronous operations. In other words, when performing asynchronous tasks, you can pass result data or error exceptions by triggering multiple success or failure events. StreamCommonly used in asynchronous task scenarios where data is read multiple times, such as network content downloading, file reading and writing, etc. for example:

Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 抛出一个异常
  Future.delayed(new Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒后返回结果
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

The above code will output in turn:

I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3

The code is very simple, so I won't repeat it.

Question: Since Stream can receive multiple events, can you use Stream to implement a subscriber-mode event bus?

to sum up

Through the above introduction, I believe you should have a preliminary impression of Dart. Since the author also uses Java and JavaScript at ordinary times, I will talk about my views based on my own experience and combining Java and JavaScript.

The reason why Dart is compared with Java and JavaScript is that they are typical representatives of strongly typed languages ​​and weakly typed languages ​​respectively, and many places in Dart's grammar also draw on Java and JavaScript.

Dart vs Java

Objectively speaking, Dart is indeed more expressive than Java at the grammatical level; at the VM level, Dart VM has been repeatedly optimized for memory recovery and throughput. However, for specific performance comparisons, the author did not find relevant test data, but In my opinion, as long as the Dart language can be popular, there is no need to worry about the performance of the VM. After all, Google already has a lot of technology on go (useless vm but with GC), javascript (v8), dalvik (java vm on android) Accumulation. It is worth noting that Dart can already achieve GC within 10ms in Flutter, so comparing Dart and Java, the decisive factor will not be performance. At the grammatical level, Dart is more expressive than Java. The most important thing is that Dart supports functional programming far stronger than Java (currently only staying at lamda expressions). The real shortcoming of Dart is ecology , but the author I believe that with the gradual popularity of Flutter, it will go back and push the accelerated development of the Dart ecosystem. For Dart, it takes time now.

Dart vs JavaScript

JavaScript's weak types have been caught short, so TypeScript, Coffeescript, and even Facebook's flow (although not a superset of JavaScript, it also provides static type checking through annotation and packaging tools). Among the scripting languages I have used (I have used Python and PHP), JavaScript is undoubtedly the scripting language with the best dynamic support. For example, in JavaScript, any object can be dynamically extended at any time. For those who are proficient in JavaScript For masters, this is undoubtedly a sharp sword. However, everything has two sides. The powerful dynamic feature of JavaScript is also a double-edged sword. You can often hear another voice, thinking that the dynamic nature of JavaScript is terrible. Too flexible makes the code difficult to predict. , Cannot restrict undesired modifications. After all, some people are always worried about the code written by themselves or others. They want to make the code controllable and expect a static type checking system to help them reduce errors. For this reason, in Flutter, Dart almost gave up the dynamic features of the scripting language, such as not supporting reflection or dynamically creating functions. And Dart forced to open the type check (Strong Mode) in 2.0, the original checked mode (checked mode) and optional type (optional type) will fade out, so in terms of type safety, Dart and TypeScript, Coffeescript are similar , So from this point of view alone, Dart does not have any obvious advantages, but on the whole, Dart can perform server-side scripting, APP development, and web development, which has advantages!

 

 

 

Guess you like

Origin blog.csdn.net/MYBOYER/article/details/89676545