我只是個Javascript初新者,寫出來的可能也會務人子弟。
只是對Javascript的Asynchronous Programming Model有興趣,紀錄一下。
Callback Hell
在最初的Javascript的設計,程式只能這樣寫。一層又一層的Callback。不蛋難以維護,並且容易出錯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function wait(func, val) { return setTimeout(() => { func(val); }, 1000); } function asyncTask() { var val = 0; wait((val) => { console.log("Step 1"); console.log(val); wait((val) => { console.log("Step 2"); console.log(val); wait((val) => { console.log("Step 3"); console.log(val); wait((val) => { console.log("Finish"); console.log(val); }, val + 4); }, val + 3); }, val + 2); }, val + 1); }
asyncTask();
|
ES6之後引季Promise,可以將Callback從水平拓展變成垂直拓展,讓程式碼更像是一般Synchronous Programming的方式
Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| function wait(func, val) { return setTimeout(() => { func(val); }, 1000); } function finish(val) { console.log("Finish"); console.log(val); } function step3(val) { console.log("Step 3"); console.log(val); } function step2(val) { console.log("Step 2"); console.log(val); } function step1(val) { console.log("Step 1"); console.log(val); } function waitPromise(func, val) { return new Promise((resolve, reject) => { wait(() => { func(val); resolve(val); }); }); } function asyncTask() { var val = 0; return waitPromise(step1, val + 1).then((val) => { return waitPromise(step2, val + 2); }).then((val) => { return waitPromise(step3, val + 3); }).then((val) => { return waitPromise(finish, val + 4); }); }
asyncTask();
asyncTask();
|
Generator
不過ES6同樣引入了Generator,可以用Generator模擬Synchronous Programming的方式
asyncTaskFlow是個Generator就是控制流,只是我們要以這個順序執行
比起Promise更像是我們熟悉的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function wait(func) { return setTimeout(func, 1000); } function finish() { console.log("Finish"); } function step3() { console.log("Step 3"); } function step2() { console.log("Step 2"); } function step1() { console.log("Step 1"); } function* asyncTaskFlow() { yield wait(step1); yield wait(step2); yield wait(step3); yield wait(finish); } function asyncTask() { var gen = asyncTaskFlow(); for (v of gen) ; }
asyncTask();
|
Async & Await
跟C#一樣,用Async和Awit作為最終解決方案,雖然這還處在ES7 Draft,不過日後應該會被採用
可以參考 ES7 async functions和Simplifying Asynchronous Coding with ES7 Async Functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| async function finish(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Finish"); resolve(val + 4); }); }); } async function step3(val ) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 3"); resolve(val + 3); }); }); } async function step2(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 2"); resolve(val + 2); }); }); } async function step1(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 1"); resolve(val + 1); }); }); } async function asyncTask() { var result = await step1(0);; console.log(result); result = await step2(result); console.log(result); result = await step3(result); console.log(result); result = await finish(result); console.log(result); } asyncTask();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| function wait(func) { return setTimeout(func, 1000); } async function finish(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Finish"); resolve(val + 4); }); }); } async function step3(val ) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 3 failed"); reject("Cancel operation"); }); }); } async function step2(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 2"); resolve(val + 2); }); }); } async function step1(val) { return new Promise((resolve, reject) => { wait(() => { console.log("Step 1"); resolve(val + 1); }); }); } async function asyncTask() { try { var result = await step1(0);; console.log(result); result = await step2(result); console.log(result); result = await step3(result); console.log(result); result = await finish(result); console.log(result); } catch (err) { console.log(err); } } asyncTask();
|