关于前端:一个-JSer-的-Dart-学习日志四异步编程

8次阅读

共计 4939 个字符,预计需要花费 13 分钟才能阅读完成。

本文是“一个 JSer 的 Dart 学习日志”系列的第四篇,本系列文章次要以开掘 JS 与 Dart 异同点的形式,在温习和坚固 JS 的同时安稳地过渡到 Dart 语言。
鉴于作者尚属 Dart 初学者,所以意识可能会比拟浮浅和全面,如您慧眼识虫,心愿不吝指正。
如无非凡阐明,本文中 JS 蕴含了自 ES5 至 ES2021 的全副个性,Dart 版本则为 2.0 以上版本。

Google 本来是将 Dart 作为 JS 的继任者来开发的,因而在设计上借鉴了许多 JS 的个性,例如事件驱动和单线程,这使得它们的异步编程写法也十分相似。

一. 应用回调函数

共同之处

  • 无论是 JS 还是 Dart,都秉承着“所有皆对象”的理念,函数概莫能外,将一个函数作为参数传递,以期在合适机会调用,是最简略的异步编程计划,此函数即 回调函数

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

不同之处

  • Dart 的异步过程与 JS 不一样——它是阻塞的,仅靠回调函数并不能解决问题,因而 回调函数格调的异步接口是 JS 的特色(就这两门语言而言)

Dart 特有的

1. 回调函数的类型

  • 在定义一个函数的时候,回调函数是其参数之一参数,而函数的参数是能够申明类型的,回调函数概莫能外。不过因为函数的因素较多,与其余类型的申明形式不一样——
    其余类型:类型关键字在变量名后面

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


    函数类型:函数的类型是 Function,但申明形参类型为函数的语法并不是 Function callback

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

二. 应用生成器函数

ES6 退出了生成器函数,能够临时中断执行,并在“唤醒”之后继续执行,这个个性与异步过程井水不犯河水,因而生成器函数也被用于解决异步过程。
Dart 中也有生成器函数,在概念上与 JS 生成器相似,但在语法和应用形式上有很多不同。

共同之处

1. 应用 *yeild

  • 应用 * 申明一个生成器函数,应用 yield 关键字暂停函数,并生成值。

    > /* 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

    生成器函数实用于一些反复触发调用的场景,例如 WebSocket 的接口事件。

2.“惰性”求值

  • 生成器函数执行遇到 yield 即停止,并返回一个特定对象,调用此对象的特定办法,函数才会持续运行,直到遇到下一个 yield

不同之处

1. Dart 生成器有同步与异步之分

  • JS 只有一种生成器,返回的可迭代对象都是 Generator
  • Dart 的生成器分为同步(sync)与异步(async),同步生成器函数返回 Iterator 实例,异步生成器返回 Stream 实例。

三. Future VS Promise

共同之处

1. 包装 async 函数的返回值

尽管异步编程的历史还算悠久,但异步函数却是一个年老的概念,所以在编程语言中引入异步函数的第一个问题就是:如何将异步函数嵌入到同步的逻辑流里?

对于这个问题,JS 给出的答案是 Promise,相应地,Dart 的答案是 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 办法和链式调用

两种计划都应用 .then 语法订阅异步过程的最终后果:

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


并且,.then办法也会返回一个新的 Promise/Future,订阅此返回值能够取得回调函数的返回值(或回调函数返回的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

不同之处

1. Dart 类型标注

在本系列文章中,Dart 的这个特点算是陈词滥调了,Dart中应用泛型语法束缚 Future 及其所包装的值的类型:

Future<int> foo async {return 1;}

2. Promise.all vs Future.wait

这个一时不晓得该算共同点还是不同点,因为语法完全一致,仅仅是关键字不同而已:

> /*  JS  */                        | // Dart
> Promise.all([                     | Future.wait([>   plus1(),                        |   plus1(),
>   plus1()                         |   plus1()
> ]).then(|]).then(>   () => console.log('All done');  |   () => print('All done');
> );                                | );

3. 构造函数的参数不同

传入的函数参数模式不一样

二者都须要传入一个函数,然而这个函数的模式不太一样。

  • Promiseexcutor 有两个地位参数:resolverejectPromise所“包装”的值即 resolve 函数的返回值;
  • Futurecomputation 函数则没有参数,Future所包装的正是 computation 的返回值。

    > /*  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 默认异步执行

  • Promiseexcutor 用来初始化 Promise,并且 JS 的异步过程不会阻塞,所以是同步执行的;
  • Futurecomputation 间接用来获取值,是异步执行的:

    > /*  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

  • 如果想同步执行 computation,应应用命名构造函数Future.sync

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

4. 包装值与谬误

  • JS 应用 Promise.resolve(value)value 包装在一个 Promise 中,用Promise.reject(error) 包装谬误error
  • Dart 的 Future.value(value)Future.error(error) 别离实现上述性能。

其实我不晓得这两种包装有什么用。

5. Future 承当了更多异步编程的工作

Future.delayed VS window.setTimeout

  • JS 应用顶层对象提供的 setTimeout 接口注册延时工作,这是一个回调格调的接口;
  • Dart 应用命名构造函数 Future.delayed 注册延时工作:

    > /*  JS  */                            | // Dart
    > var task = setTimeout(                | var task = Future.delayed(>   () => {|   Duration(milliseconds: 100),
    >     console.log('done');              |   () {>},                                  |     print('done');
    >   100                                 |   }
    > };                                    | };

    Dart 应用 Duration 类来结构时间差,比 JS 默认的毫秒数要直观得多(然而写起来多少有点麻烦,不晓得有没有语法糖)。

Future.microstack VS Promise.resolve().then

  • JS 中注册微工作最便捷的计划是 Promise.resolve().then,(当然,前提是应用运行时提供的Promise 或者靠谱的 polyfill 计划),尽管“便捷”,但毕竟只是一种 trick;
  • 而 Dart 提供了专门的接口 Future.microtask 来注册微工作:

    > /*  JS  */                            | // Dart
    > function register(task){| register(task){
    >   Promise.resolve.then(               |   Future.microtask(
    >     task                              |     task
    >   );                                  |   );
    > }                                     | }

    好在绝大多数状况下,一般的开发者不须要开发者本人调度工作优先级,因而 JS 的这个写法无关紧要,只有在面试的时候不要掉链子就行。

6. Promise 有更多丰盛的性能

  • 相熟 Promise 的人不会对 Promise.allSettlePromise.racePromise.any 这些静态方法感到生疏,而这些办法是 Future 所不具备的,心愿早日能在 Dart 里见到它们。

    JS 总算扳回一局!

四. async/await

如果你问我最喜爱自 ES6 以来退出的哪个新个性,那毫无疑问是 ES2017 带来的 async/await 语法和 ES2015 带来的解构语法。
而在 Dart 中,async/await这一神兵利器也没有缺席!

7. Futuredart:async 包提供的性能

  • 如欲应用 Future(以及),该当先引入dart:async 包。

    然而在 Dartpad 中不引入也能够应用。

相同之处

用法根本类似

  • Talk is cheap, here is the code:

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

    .

不同之处

1. async 关键字的地位

  • 在 JS 中,async置于函数申明语句的后面;
  • 在 Dart 中,async置于函数参数列表的前面。

    这个区别在下面的例子中曾经有所体现。

TODO: 须要略微钻研下 Dart 构造函数初始化实例变量的时候,async 放哪里。所以这里总结的地位不肯定是对的。

2. 别离返回 PromiseFuture

  • 在 JS 中,async函数返回 Promise 实例;
  • 在 Dart 中,async函数返回 Future 实例。

两品种的差别在上一节曾经说明(至多作者本人感觉是说明了),因而不再赘述。

正文完
 0