CancellationToken
Abort async function in ES2016
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.29/browser-polyfill.min.js"></script>
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<div>
<button data-bind="click: start">Start</button>
<button data-bind="click: cancel">Cancel</button>
</div>
<i class="fa fa-square-o fa-spin fa-2x"
data-bind="visible: running"></i>
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
<p data-bind="text: result"></p>
Babel + JSX
class ViewModel {
duration = ko.observable(5000);
items = ko.observableArray([]);
running = ko.observable(false);
result = ko.observable("");
cancellationToken = null;
// start ボタンクリックで呼び出されるメソッド
async start() {
this.items([]); // 初期化
this.result(""); // 初期化
this.running(true); // 実行中にする(アイコンが回転)
// トークンを作成してフィールドにセット
this.cancellationToken = new CancellationToken();
try {
for (var i = 0; i < 5; i++) { // 5回まで
const before = Date.now()
await delay(1000, this.cancellationToken); // トークンを渡して呼び出しconst after = Date.now()
this.items.push("running " + (i + 1)); // running n を追加
}
this.result("DONE!"); // キャンセルされなかったらこれが呼び出される
} catch (e) {
// キャンセルされるとここに飛ぶ
this.result(e.cancelled ? 'CANCELLED!' : e.message);
}
this.cancellationToken = null; // トークンを除去
this.running(false); // 実行中を解除(アイコンが消える)
}
// cancel ボタンクリックで呼び出されるメソッド
async cancel() {
// トークンがフィールドにあればキャンセルを呼び出す
if (this.cancellationToken) {
this.cancellationToken.cancel();
}
}
}
function delay(duration, cancellationToken = null) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
if (cancellationToken) {
cancellationToken.register(reject);
}
});
}
/**
* cancellation token to abort async function
* @see http://stackoverflow.com/questions/32897385/abort-ecmascript7-async-function
*/
class CancellationToken {
isCancellationRequested = false;
constructor(parentToken = null) {
this.cancellationPromise = new Promise(resolve => {
this.cancel = e => {
this.isCancellationRequested = true;
if (e) {
resolve(e);
} else {
var err = new Error("cancelled");
err.cancelled = true;
resolve(err);
}
}
});
if (parentToken && parentToken instanceof CancellationToken) {
parentToken.register(this.cancel);
}
}
register(callback) {
...