共计 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. 构造函数的参数不同
传入的函数参数模式不一样
二者都须要传入一个函数,然而这个函数的模式不太一样。
Promise
的excutor
有两个地位参数:resolve
和reject
。Promise
所“包装”的值即resolve
函数的返回值;-
Future
的computation
函数则没有参数,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
默认异步执行
Promise
的excutor
用来初始化Promise
,并且 JS 的异步过程不会阻塞,所以是同步执行的;-
Future
的computation
间接用来获取值,是异步执行的:> /* 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.allSettle
、Promise.race
、Promise.any
这些静态方法感到生疏,而这些办法是Future
所不具备的,心愿早日能在 Dart 里见到它们。JS 总算扳回一局!
四. async/await
如果你问我最喜爱自
ES6
以来退出的哪个新个性,那毫无疑问是ES2017
带来的async/await
语法和ES2015
带来的解构语法。
而在 Dart 中,async/await
这一神兵利器也没有缺席!
7. Future
是 dart: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. 别离返回 Promise
和Future
- 在 JS 中,
async
函数返回Promise
实例; - 在 Dart 中,
async
函数返回Future
实例。
两品种的差别在上一节曾经说明(至多作者本人感觉是说明了),因而不再赘述。