Skip to content

Commit

Permalink
為 App 的所有事件都加上 Promise
Browse files Browse the repository at this point in the history
我測試寫到一半的時候,發現我的事件都都無法測試:因為在處理事件的過程式會
發非同步的 AJAX 請求,所以我無法知道這個事件處理程式完成了沒有。解決方法
有兩個

1. 用 setTimeout 延遲一段時間來等待事件處理完畢。這個做法很差。因為這個
   做法的語意是「我不知道什麼時候做完,我只是猜這樣應該能行」。測試不應
   該用猜測的;事實上,如果你想寫出一個穩定的程式,那就不應該用猜的。
2. 用 Promise

但是加上 Promise 會讓程式有額外的複雜度,讓程式更難理解。如何判斷利弊呢?
我回頭考慮,發現加上 Promise 會讓事件的語義更加完整:原本的程式裡,當觸
發 submitPincode 事件之後,你不知道這個事件到底處理的如何了。加上
Promise 之後,我們就知道事件處理的結果如何,也可以針對處理失敗的情況做補
救或是顯示必要的資訊給使用者。

這些事件是和其他 component 互動使用的,也就是所謂的
protocol/interface/API 的意思。所以如果能讓 API 的語意更加完整,增加複雜
度應當是可以接受的:語意完整的話,你不必追入處理程式內就能判斷錯誤是不是
在這裡產生的。日後要給其他 component 用的時候也可以輕易理解每個東西的用
途,這是 self-explain 的概念XD

當然,任何事都有代價:語意清楚的程式有時會囉嗦或複雜一點XD
  • Loading branch information
Ronmi committed Jul 24, 2016
1 parent 146bfad commit 5ee4337
Showing 1 changed file with 59 additions and 42 deletions.
101 changes: 59 additions & 42 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// <reference path="../typings/globals/es6-shim/index.d.ts" />

import * as React from "react";
import { OrderData, translate } from "./types";
import AuthForm from "./components/AuthForm";
Expand Down Expand Up @@ -31,30 +33,36 @@ export default class App extends React.Component<Props, State> {
};
}

// custom event handlers
submitPincode(pin: string) {
this.props.api.Auth(pin).then(
() => {
this.setState({authed: true});
this.codeSelected(this.state.code);
},
() => {
this.props.errHandler("認證失敗");
}
);
// custom event handlers, returns promise so we can test on it
submitPincode(pin: string): Promise<void> {
return new Promise<void>((res, rej) => {
this.props.api.Auth(pin).then(
() => {
this.setState({ authed: true });
this.codeSelected(this.state.code).then(() => { res(); }, () => { res(); });
},
() => {
this.props.errHandler("認證失敗");
rej();
}
);
});
}
submitOrder(order: OrderData) {
submitOrder(order: OrderData): Promise<void> {
let data = this.state.data;
this.addOrder(order)
this.props.api.Add(order).then(
() => { /* do nothing */ },
() => {
this.setState({ data: data });
}
);
return new Promise<void>((res, rej) => {
this.props.api.Add(order).then(
() => { res(); },
() => {
this.setState({ data: data });
rej();
}
);
});
}
codeSelected(code: string) {
this.getOrderList(code);
codeSelected(code: string): Promise<void> {
return this.getOrderList(code);
}

// helper methods
Expand All @@ -68,42 +76,51 @@ export default class App extends React.Component<Props, State> {
data.sort(sorter);
this.setState({ data: data });
}
getOrderList(code: string) {
getOrderList(code: string): Promise<void> {
if (code == translate(code)) {
this.getAllOrderList();
return;
return this.getAllOrderList();
}

this.props.api.List(code).then(
(data: OrderData[]) => {
data.sort(sorter);
this.setState({ code: code, data: data });
}
);
return new Promise<void>((res, rej) => {
this.props.api.List(code).then(
(data: OrderData[]) => {
data.sort(sorter);
this.setState({ code: code, data: data });
res();
},
() => { rej(); }
);
});
}
getAllOrderList() {
this.props.api.ListAll().then(
(data: OrderData[]) => {
data.sort(sorter);
this.setState({ code: "", data: data });
}
);
getAllOrderList(): Promise<void> {
return new Promise<void>((res, rej) => {
this.props.api.ListAll().then(
(data: OrderData[]) => {
data.sort(sorter);
this.setState({ code: "", data: data });
res();
},
() => { rej(); }
);
});
}

render() {
if (!this.state.authed) {
return (
<AuthForm
submitPincode={this.submitPincode.bind(this)}
error={this.props.errHandler} />
);
<div>
<AuthForm
submitPincode={this.submitPincode.bind(this)}
error={this.props.errHandler} />
</div>
);
}

return (
<div>
<OrderForm
submitOrder={this.submitOrder.bind(this)}
error={this.props.errHandler} />
submitOrder={this.submitOrder.bind(this)}
error={this.props.errHandler} />
<div className="list-type">
<CurrencySelector
codeSelected={this.codeSelected.bind(this)}
Expand Down

0 comments on commit 5ee4337

Please sign in to comment.