头图

This article is the fourth in the series of "A JSer's Dart Learning Log". This series of articles mainly aims to explore the similarities and differences between JS and Dart, while reviewing and consolidating JS while smoothly transitioning to the Dart language.
Since the author is still a beginner to Dart, his understanding may be superficial and one-sided. If you know the worms, I hope you can correct me.
Unless otherwise specified, the JS in this article contains all the features from ES5 to ES2021, and the Dart version is 2.0 or higher.

Google originally developed Dart as the successor of JS, so it has borrowed many features of JS in its design, such as event-driven and single-threaded, which makes their asynchronous programming approach very similar.

1. Use callback function

Common ground

  • Both JS and Dart are adhering to the concept of "everything is an object". There is nothing more than a function. Passing a function as a parameter in order to call it at the right time is the simplest asynchronous programming solution. This function is the callback function .

    > /* ******************** Both JS and Dart ******************** */
    > var func = (param) => param + 1;
    > var caller = (callback) => callback(1);

the difference

  • Dart's asynchronous process is different from JS-it is blocking, and callback functions alone cannot solve the problem. Therefore callback function style is the characteristic of JS (for the two languages) .

Dart specific

1. Types of callback functions

  • When defining a function, the callback function is one of its parameters, and the parameters of the function can be declared in type. There is no exception to the callback function. However, due to the many elements of the function, it is different from other types of declarations——
    Other types: type keyword in front of the variable name :

    void func(int a, Map<String, dynamic> b){
      // Do something
    }


    Function type: The type of the function is Function , but the syntax for declaring the parameter type as a function is not Function callback :

    // callback 第一个参数为 `num` 类型,第二个参数为 `int`类型
    // callback 的返回值为 `String` 类型
    // 如果这些类型都未显式声明的话,则全部都是 dynamic
    void func(String callback(num a, int b)){
      // Do something
    }

2. Use the generator function

ES6 has added generator functions, which can temporarily interrupt execution and continue execution after "waking up". This feature complements asynchronous processes, so generator functions are also used to handle asynchronous processes.
Dart also has generator functions, which are similar in concept to JS generators, but there are many differences in syntax and usage.

Common ground

1. Use * and yeild

  • Use * declare a generator function, use the yield keyword to pause the function and generate the value.

    > /* JS */                          | /* Dart */
    > function* gen(max) {              | gen(int max) sync* {
    >     var state = 0;                  |   var state = 0;
    >   while(state < max) {            |   while(state < max) {
    >     yield state ++;               |     yeild state ++;
    >   }                               |   }
    > }                                 | }
    > const reader = gen(6);            | final reader = gen(6);
    > console.log(reader.next().value); | print('${render.elementAt(0)}');
    > // 0                              | // 0
    The generator function is suitable for some scenarios where the call is repeatedly triggered, such as the interface event of WebSocket

2. "Lazy" evaluation

  • The generator function execution yield , and returns a specific object. Calling a specific method of this object, the function will continue to run until it encounters the next yield .

the difference

1. Dart generators are divided into synchronous and asynchronous

  • JS has only one kind of generator, and the returned iterable objects are all Generator ;
  • Dart generators are divided into synchronous ( sync ) and asynchronous ( async ). The synchronous generator function returns the Iterator instance, and the asynchronous generator returns the Stream instance.

3. Future VS Promise

Common ground

1. Wrap the return value of the async

Although the history of asynchronous programming is quite long, asynchronous functions are a young concept, so the first question of introducing asynchronous functions into programming languages is: How to embed asynchronous functions into synchronous logic flow?

For this question, the answer given by JS is Promise , correspondingly, Dart's answer is Future :

> /*  JS  */                          |  // Dart
> async function asyncFunc() {        | asyncFunc() async {
>   return await 'yes';               |   return await 'yes';
> }                                   | }
> console.log(asyncFunc());           | print(asyncFunc());
> // Promise {<pending>}              | // Instance of '_Future<dynamic>'

2. .then method and chain call

Both schemes use .then syntax to subscribe to the final result of the asynchronous process:

>  /* JS */                          |  // Dart
> asyncFunc().then(                  | asyncFunc().then(
>   (res) =>                         |   (res) =>
>     console.log(`Got ${res}`)      |     print('Got $res')
> )                                  | )
> // Got yes                         | // Got yes


In addition, the .then method will also return a new Promise/Future , subscribe to this return value to get the return value of the callback function (or the value Promise/Future

> /*  JS  */                         | // Dart
> async function plus1(v = 0) {      | int plus1(int v) async {
>   return await v + 1;              |   return await v + 1;
> }                                  | }
> function plus2(v = 0) {            | int plus2(int v) {
>   return v + 2;                    |   return v + 2;
> }                                  | }
> plus1().then(plus1)                | plus1().then(plus1)
>   .then(plus2).then(console.log);  |   .then(plus2).then(print);
> // 4                               | // 4

the difference

1. Dart type annotation

In this series of articles, this feature of Dart is a cliché. The generic syntax used in Dart Future and the type of the value it wraps:
Future<int> foo async {
  return 1;
}

2. Promise.all vs Future.wait

For a while, I don’t know if it should be regarded as common or different, because the grammar is exactly the same, only the keywords are different:
> /*  JS  */                        | // Dart
> Promise.all([                     | Future.wait([
>   plus1(),                        |   plus1(),
>   plus1()                         |   plus1()
> ]).then(                          | ]).then(
>   () => console.log('All done');  |   () => print('All done');
> );                                | );

3. The parameters of the constructor are different

The function parameters passed in are in different forms
Both need to pass in a function, but the form of this function is different.
  • Promise of excutor has two positional parameters: resolve and reject . Promise the "packaging" value, that resolve return value of the function;
  • Future of computation function is no argument, Future packaged precisely computation return value.

    > /*  JS  */                             | // Dart
    > const a = new Promise(                 | final a = /*new*/ Future(
    >   (resolve, reject) => resolve(1)      |   () => 1
    > );                                     | );
    > console.log(await a);                  | print(await a);
    > // 1                                   | // 1
computation default asynchronous execution
  • Promise of excutor used to initialize Promise , and the asynchronous process of JS will not be blocked, so it is executed synchronously;
  • Future of computation directly used to obtain the value, which is executed asynchronously:

    > /*  JS  */                             | // Dart
    > var mut = 0;                           | var mut = 0;
    > const a = new Promise(                 | final a = /*new*/ Future(
    >   function (resolve, reject) {         |   () {
    >     mut++;                             |     mut++;
    >     resolve(1);                        |     return 1;
    >   }                                    |   }
    > );                                     | );
    > console.log(mut);                      | print(mut);
    > // 1                                   | // 0

  • If you want to execute computation synchronously, you should use the named constructor Future.sync :

    int mut = 0;
    final a = Future.sync((){
      mut++;
      return mut; 
    });
    print(mut); // 1

4. Packing values and errors

  • JS uses Promise.resolve(value) to value in a Promise , and uses Promise.reject(error) package error error ;
  • Dart's Future.value(value) and Future.error(error) realize the above functions respectively.
Actually I don't know what is the use of these two kinds of packaging.

5. Future undertakes more tasks of asynchronous programming

Future.delayed VS window.setTimeout
  • setTimeout interface provided by the top-level object to register the delayed task, which is a callback style interface;
  • Dart uses the named constructor Future.delayed register delayed tasks:

    > /*  JS  */                            | // Dart
    > var task = setTimeout(                | var task = Future.delayed(
    >   () => {                             |   Duration(milliseconds: 100),
    >     console.log('done');              |   () {
    >   },                                  |     print('done');
    >   100                                 |   }
    > };                                    | };
    Dart uses the Duration class to construct the time difference, which is much more intuitive than the JS default milliseconds (but it's a bit cumbersome to write, I don't know if there is syntactic sugar).
Future.microstack VS Promise.resolve().then
  • The most convenient solution for Promise.resolve().then JS is 061628f7d610b9, (of course, the premise is to use the Promise provided at runtime or the reliable polyfill solution). Although "convenient", it is just a trick after all;
  • And Dart provides a special interface Future.microtask to register microtasks:

    > /*  JS  */                            | // Dart
    > function register(task){              | register(task){
    >   Promise.resolve.then(               |   Future.microtask(
    >     task                              |     task
    >   );                                  |   );
    > }                                     | }
    Fortunately, in most cases, ordinary developers do not need to schedule task priorities by themselves, so this way of writing JS is irrelevant, as long as you don't drop the chain during the interview.

6. Promise has more rich functions

  • Promise who are familiar with 061628f7d6116d will not be unfamiliar with the static methods Promise.allSettle , Promise.race , Promise.any Future , I hope to see them in Dart soon.

    JS finally got a game back!

Four. async/await

If you ask my favorite from ES6 which new features added since, that there is no doubt ES2017 brought async/await grammar and ES2015 bring deconstruction syntax.
And in Dart, async/await a magic weapon, is not absent!

7. Future is the function provided by the dart:async

  • If you want to use Future (and), you should first introduce the dart:async package.

    However, it can be used without introducing it in Dartpad.

Similarities

Basically similar in usage

  • Talk is cheap, here is the code:

    > /*  JS  */                       | // Dart
    > async function foo(){            | foo () async {
    >   return await asyncFunc();      |   return await asyncFunc();
    > }                                | {

the difference

1. The location of the async

  • In JS, async placed in front of the function declaration statement;
  • In Dart, async placed at the end of the function parameter list.

    This difference has been reflected in the above example.
TODO: I need to study a little bit about where async placed when the Dart constructor initializes instance variables. So the position summarized here is not necessarily correct.

2. Return Promise and Future

  • In JS, async function returns Promise instance;
  • In Dart, the async function returns Future instance of 061628f7d613da.
The difference between the two types has been clarified in the previous section (at least the author thinks it is clarified), so I won't repeat it.

知名喷子
5.9k 声望2.5k 粉丝

话糙码不糙。