終於把第6季新手村寫完了,再次向Angular Taiwan每位管理人員及講者致敬
再來是我個人非常有興趣的「測試」
因為寫測試是好習慣、是基礎,是拿來保護程式的,
驗收測試
,再細分定出整合測試
、驗收測試
,等通過測試,就是通過驗收了(喔~~YA!!!)訂定
驗收測試
後的需求異動都視為後續功能擴充或維護(想像中)
供需雙方的遊戲規則要明確合理,軟體產業才會正成長,共創雙贏
以下開放想像…
[S03E01] RxJS & Promise
https://www.youtube.com/watch?v=DZk9isnp0zc&list=PL9LUW6O9WZqgUMHwDsKQf3prtqVvjGZ6S&index=43
本集由Kiven大大講解RxJS & Promise,的結合使用,可能這對寫測試來說很重要、很基本吧
由整個第5季專門講RxJS就可以看出多重要
常用的包含Observable跟Operator,Observable可以看成是source來源
source來源可能是從Event、Array、Promise轉換成Observable
import {Observable} from 'rxjs/Observable';
...
constructor(private http: HttpClient){
// 最基本的Observable
const source$ = Observable.interval(1000)
^ Observable習慣用$命令,沒特殊效果
.take(10)
.map( x => x*100 );
// 訂閱
source$.subscribe(
data => console.log(data), // next
err => console.error(err),
() => console.log('complete')
);
// 0 -> 100 -> 200 -> … -> 900 -> complete
}
只要RxJS裡的Operator能接受Observable,就能夠接受promise
例如: mergeMap、switchMap()、folkJoin
(可以吃Observable),改餵promise
參考文章:
30 天精通 RxJS(18): Observable Operators - switchMap, mergeMap, concatMap
https://ithelp.ithome.com.tw/articles/10188387
function promiseDelay(ms){
return new Promise(resolve => {
setTimeout(() => resolve('done'),ms);
});
}
import {Observable} from 'rxjs/Observable';
...
constructor(private http: HttpClient){
// 最基本的Observable
const source$ = Observable.interval(1000)
.take(10)
.map( x => x*100 )
.mergeMap(promiseDelay);
^^^^^^^^^^^^ 會接受promise的值
// 訂閱
source$.subscribe(
data => console.log(data), // data會變成'done'
err => console.error(err),
() => console.log('complete')
);
// done -> done -> done -> … -> done -> complete
}
const source$ = Observable.interval(1000)
.take(10)
.map( x => x*100 )
.mergeMap(promiseDelay)
.retry(3);
通常subscribe會回傳subscription
可以透過subscription,去手動取消observable的行為
Observable 的 forEach 的效果跟subscribe相同,不過forEach當complete時會回傳promise
source$.subscribe(next,err,complete)
// (method) Observable<{}>.forEach(next: (value:{}) => void,
// PromiseCtor ?: PromiseConstructor): Promise<void>
var promiseVar = source$.forEach((data) => {
data => console.log(data);
});
透過forEach() complete時會回傳promise,可以搭配await、async
async test(){
// await會等到this.source$.forEach的Promise跑完,才會往下跑
// await、async讓非同步的行為,表現的跟同步一樣
await this.source$.forEach(data => {
^^^^^ await後面的promise跑完,才會往下跑
console.log(data);
});
console.log('complete');
^^^^^^^^^^^^^^^^^^^^^^
}
如果沒有用await、async
ngOnInit(){
this.test();
}
test(){
this.source$.forEach(data => {
console.log(data); // 2、再跑這一行(因為每1000ms跑一次)
});
console.log('complete'); // 1、會先跑這一行
}
// complete -> 0 -> 100 -> ...
testWithoutAwaitAsync(){
this.http
.get<any[]>('https://jsonplaceholder.typecode.com/posts')
.subscribe(data => {
this.postData = data;
// 對http get回來的data 加工
});
// 如果沒有await、async,subscribe後面可能會先執行,通常這不是我們想要的
console.log(this.postData);
}
// 使用 await、async,簡單改寫
// async只能用在promise,而且他還會再回傳promise
async testWithAwaitAsync(){
^^^^^
await this.http
^^^^^
.get<any[]>('https://jsonplaceholder.typecode.com/posts')
.forEach(data => {
^^^^^^^^
this.postData = data;
// 對http get回來的data 加工
});
// 如果沒有await、async,subscribe後面可能會先執行,通常這不是我們想要的
console.log(this.postData);
}
Promise會告知:(丟給外面的接)
promiseDelay(123).then( data => console.log(data));
^^^^^^^ ^^^^ 成功後, ^^^^ 針對成功的結果,做事情
// 很容易處理非同步行為
test2(){
Observable.of(1).merge(async x => await this.test());
$$$$$$$$$$$$$$$ ^^^^^ aaaaa bbbbb
很漂亮的串接成一行
}
await、async發生錯誤時是完全安靜的
必須用try catch去包awiat
https://stackoverflow.com/questions/40884153/try-catch-blocks-with-async-await
https://blog.othree.net/log/2019/01/26/async-await-try-catch/
引用第1篇
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch (error) {
console.error(error);
}
}
would be something like this, using promises explicitly:
function main() {
getQuote().then((quote) => {
console.log(quote);
}).catch((error) => {
console.error(error);
});
}
or something like this, using continuation passing style:
function main() {
getQuote((error, quote) => {
if (error) {
console.error(error);
} else {
console.log(quote);
}
});
}
例如:cordova api回傳promise,如果用Observable.fromPromise去包promise很難看
var source = Rx.Observable.fromPromise(promise);
很多API回傳的是promise,
可以用mergeMap、switchMap直接接promise,
方便後續Rx操作,而且不用再多包一層
https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md
有一些operator改名字了
do -> tap
catch -> catchError
switch -> switchAll
finally -> finalize
tree-shaking做得比較好
早期的RxJS要這麼加
import 'rxjs/add/operator/map';
變成這樣
import { map, filter, sacn } from 'rxjs/operators';
接受Observable,回傳Observable
可以把多個RxJS的operator寫在let裡,可以簡化程式碼
沒使用let
test(){
return this.http
.get<Post[]>('https://jsonplaceholder.typicode.com/posts')
.mergeMap(x => x)
.fileter(x => x.id >10)
.map(x => x.title + '!!!'));
}
// 使用let
test(){
return this.http
.get<Post[]>('https://jsonplaceholder.typicode.com/posts')
.mergeMap(x => x)
.let(obs => obs.fileter(x => x.id >10).map(x => x.title + '!!!'));
^^^ 目前的observable
}
// 也可以餵一個function給let
// 使用let
test(){
return this.http
.get<Post[]>('https://jsonplaceholder.typicode.com/posts')
.mergeMap(x => x)
.let(this.processMethod());
^^^^^^^^^^^^^^^^^^^
}
// 可以把多個RxJS的operator寫在一個function
processMethod() {
return o => o.fileter(x => x.id >10).map(x => x.title + '!!!');
^ 從let傳入
}
跟let很像,但用,
串接
https://angular.io/guide/rx-library
https://blog.kevinyang.net/2018/08/28/rxjs-custom-operator/
讓operator跟observable脫離,方便組合operators
source$.pipe(
filter(x => x%2 === 0),
map(x => x+x),
scan((acc,x) => acc + x, 0)
)
.subscribe(x => console.log(x))
interval(1000).pipe(
takeEveryNth(2),
map(x => x+x),
takeEveryNth(3),
take(3),
toArray()
)
// 在html的用法 postData$ | async
postData$ = this.http
.get<Post[]>('https://jsonplaceholder.typicode.com/posts')
.mergeMap(x => x)
.let(this.processMethod());
^^^^^^^^^^^^^^^^^^^^
// 如果service回傳的是observable,就可以串成指令,不用再包成function
// 包function會有副作用,例如.share會出問題
// source$ = Observalbe.interval(1000).take(1).map(x => x*100).share();
// 包含function跟直接串成變數,結果不一樣
processMethod() {
return o => o.fileter(x => x.id >10).map(x => x.title + '!!!');
}