需求:javascript
A、依次讀取 A|B|C 三個文件,若是有失敗,則當即終止。
B、同時讀取 A|B|C 三個文件,若是有失敗,則當即終止。java
需求A
:node
let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function (callback) { if (read(1)) { return callback(null, "111"); } else { return callback("a fail"); } } let readFileB = function (callback) { if (read(1)) { return callback(null, "222"); } else { return callback("b fail"); } } let readFileC = function (callback) { if (read(1)) { return callback(null, "333"); } else { return callback("c fail"); } } readFileA(function (err, data) { if (err) { console.log("open file " + err); return; } console.log("讀取 a.txt 成功!內容:" + data); readFileB(function (err, data) { if (err) { console.log("open file " + err); return; } console.log("讀取 b.txt 成功!內容:" + data); readFileC(function (err, data) { if (err) { console.log("open file " + err); return; } console.log("讀取 c.txt 成功!內容:" + data); }); }); });
return:segmentfault
讀取 a.txt 成功!內容:111 讀取 b.txt 成功!內容:222 讀取 c.txt 成功!內容:333
需求B
:太噁心了,不寫了,總之很繁瑣.數組
async.js
庫的詳細介紹能夠見:[待寫]promise
需求A
:異步
async.series
async
var async = require("async"); let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function (callback) { if (read(1)) { return callback(null, "111"); } else { return callback("a fail"); } } let readFileB = function (callback) { if (read(0)) { return callback(null, "222"); } else { return callback("b fail"); } } let readFileC = function (callback) { if (read(1)) { return callback(null, "333"); } else { return callback("c fail"); } } async.series([readFileA, readFileB, readFileC], function (err, datas) { if (err) { console.log("open file " + err); } console.log(datas); return; });
當第二個 readFileB() 讀取失敗的話:ide
return:函數
open file b fail [ '111', undefined ]
需求B
:
async.parallel
var async = require("async"); let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function (callback) { if (read(1)) { return callback(null, "111"); } else { return callback("a fail"); } } let readFileB = function (callback) { setTimeout(() => { if (read(0)) { return callback(null, "222"); } else { return callback("b fail"); } }, 1000); } let readFileC = function (callback) { if (read(1)) { return callback(null, "333"); } else { return callback("c fail"); } } async.parallel([readFileA, readFileB, readFileC], function (err, datas) { if (err) { console.log("open file " + err); } console.log(datas); return; });
當第二個 readFileB() 讀取失敗 (注意我給它加了 setTimeout,爲了體現跟上面串行結果的不同) 的話:
return:
open file b fail [ '111', undefined, '333' ]
總結:async.js 跟 callback 比的好處:
一、代碼量少了,解決了回調地獄金字塔的缺陷
二、async 的第二個參數回調函數裏,能夠統一處理錯誤(建議用不一樣的 Error 類做區分)
三、成功返回的結果 datas 能夠彙總到一個數組中方便處理
[拓展]
promise 知識
new Promise()
// promise 在 new 的時候已經開始運行 new Promise(() => console.log("I have already started!"));
return:
I have already started!
promise.then(successCallback, failureCallback);
new Promise((resolve, reject) => resolve()).then(function (data) { console.log("success"); }, function (data) { console.log("fail"); })
return:
success
promise.catch(failureCallback)
// promise.catch(failureCallback) 是 promise.then(null, failureCallback) 的縮略形式 new Promise((resolve, reject) => reject()).catch( function (data) { console.log("fail"); })
return:
fail
鏈式調用
// 鏈式調用的原理:then 函數會返回一個新的 promise new Promise((resolve, reject) => reject()).then(function (data) { console.log("success_1"); }, function (err) { console.log("fail_1"); }).then(function (data) { console.log("success_2"); }, function (err) { console.log("fail_2"); });
return:
fail_1 success_2
問1:
then 函數
會返回一個新的 promise,可是 then 的 successCallback 和 failureCallback 這兩個回調函數裏都無法調用 resolve() 和 reject(),那這個新的 promise 如何指定最終狀態呢?
then 的 successCallback 和 failureCallback 裏 | 等同於 |
---|---|
不返回 | resolve(undefined) |
return 1 | resolve(1) |
return Promise.resolve() | resolve() |
return Promise.reject() | reject() |
throw Error() | reject() |
return new Promise() | 以此類推 |
而普通的 promise 對象,若是不顯示調用 resolve/reject ,則沒有任何反應,例如:
new Promise((resolve, reject) => {return 1;}).then(function (data) { console.log("success"); }, function (err) { console.log("fail"); });
return:
沒有任何輸出
問2:then 函數若是
successCallback
和failureCallback
都爲 null,會發生什麼?
什麼都不會發生,.then(null, null)
只要一方爲 null,等於交給下一個 then 去接管這個回調
new Promise((resolve, reject) => reject()) .then(null, null) .then(null, null) .then(null, null) .then(null, null) .then(function (data) { console.log("success_2"); }, function (err) { console.log("fail_2"); });
因此按照上面 2 個提問揭示的規律,咱們能夠寫成下面優雅的代碼:
// 鏈式調用的原理:then 函數會返回一個新的 promise new Promise((resolve, reject) => resolve()).then((data) => { console.log("success_1"); }).then((data) => { console.log("success_2"); throw Error("error"); }).then((data) => { console.log("success_3"); }).catch((err) => { console.log(err); });
return:
success_1 success_2 Error: error ……
注:
.catch()
後還能夠繼續接.then()
或.catch()
這就達到了以下別人家同步代碼的清晰的表達:
try { let result = syncDoSomething(); let newResult = syncDoSomethingElse(result); let finalResult = syncDoThirdThing(newResult); console.log(`Got the final result: ${finalResult}`); } catch(error) { console.log(error); }
因此,需求A
:
let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("111"); } else { reject("a fail"); } }); } let readFileB = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("222"); } else { reject("b fail"); } }); } let readFileC = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("333"); } else { reject("c fail"); } }); } //[串行] 場景:依次預加載多個資源,若是中途有失敗,則進入 .catch() readFileA().then(function (data) { console.log("讀取 a.txt 成功!內容:" + data); return readFileB(); }).then(function (data) { console.log("讀取 b.txt 成功!內容:" + data); return readFileC(); }).then(function (data) { console.log("讀取 c.txt 成功!內容:" + data); return "讀取結束"; }).then(function (data) { console.log(data); return; }).catch(function (err) { console.log("open file " + err); })
promise
vs 事件監聽
a. 事件監聽
更多的是針對同一對象上發生屢次的事情(如 keyup、touchstart 等)
而 promise
更多的表現這個對象最終走向什麼狀態,且不可改變。
但有個神奇的特型是一致的,事件監聽
和 promise
均可以對同一事件的反應綁定屢次的回調函數,以下面例子所示:
let promise = new Promise((resolve, reject) => { console.log("I have already started!"); resolve(); }) setTimeout(() => { promise.then(function (data) { console.log("success_1"); throw new Error(); }, function (err) { console.log("fail_1"); }); }, 2000); setTimeout(() => { promise.then(function (data) { console.log("success_2"); }, function (err) { console.log("fail_2"); }); }, 4000);
return:
I have already started! //又等待了2秒 success_1 (node:13150) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error (node:13150) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. //又等待了2秒 success_2
b. 跟 事件監聽
不同,若是 promise 已成功或失敗了,過段時間再添加了回調函數,則仍是能夠成功調用回調。這個上面的例子也能夠體現。
c. 事件監聽
更多的關注某些功能的準確時間,promise
更多地是關注對結果做出的反應。
promise 擴展 API
Promise.resolve()
和 Promise.reject()
手動建立一個已經 resolve 或者 reject 的 promise 的快捷方法。
promise.all
:能夠實現需求B
://promise.all [並行] 場景:預加載多個資源,都完成後才能進入頁面 Promise.all([readFileA(), readFileB(), readFileC()]).then(function (datas) { console.log(datas); //全部promise都resolve,返回array return; }).catch(function (err) { console.log("open file " + err); //只要有一個promise是reject,返回這個reject的value })
promise.race
://promise.race [並行] 場景:taskA:fetch圖片,taskB:settimeout拋錯,讓兩個task賽跑實現請求超時報錯功能 Promise.race([taskA(), taskB()]).then(function (data) { //進到resolve仍是reject回調只取決於第一個肯定狀態的Promise console.log(data); return; }).catch(function (err) { console.log("讀取圖片超時"); })
總結:promise 跟 callback 比的好處:
一、代碼量少了,解決了回調地獄金字塔的缺陷
二、.catch 能夠統一處理錯誤(建議用不一樣的 Error 類做區分)
需求A
:let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("111"); } else { reject("a fail"); } }); } let readFileB = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("222"); } else { reject("b fail"); } }); } let readFileC = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("333"); } else { reject("c fail"); } }); } async function test() { try { let re_a = await readFileA(); let re_b = await readFileB(); let re_c = await readFileC(); console.log({re_a, re_b, re_c}); //若是都成功,return: { re_a: '111', re_b: '222', re_c: '333' } } catch (err) { console.log(err); // 若是b失敗,return: b fail } } test();
總結:async / await 跟 callback 比的好處:
一、代碼量最少,解決了回調地獄金字塔的缺陷(Promise 經過 then 鏈來解決 callback 多層回調金字塔的問題,如今又用 async/await 來進一步優化它)(基於 promise 的 async / await 也試圖淘汰 promise)
二、.catch 能夠統一處理錯誤(建議用不一樣的 Error 類做區分)
[拓展]
一、async 函數就是 Generator 函數的語法糖,本質上並非同步代碼
二、async 用於申明一個 function 是異步的,而 await (async wait) 用於等待一個異步方法執行完成。
三、await 只能出如今 async 函數中,因此在代碼的頂層,咱們沒法使用 await,因此添加它 .then/catch 來處理最終結果或掉落錯誤是正常的作法。
try { let re_a = await readFileA(); let re_b = await readFileB(); let re_c = await readFileC(); console.log({re_a, re_b, re_c}); } catch (err) { console.log(err); }
return:
報錯
或者頂層使用當即執行函數表達式(IIFE)
(async () => { try { let re_a = await readFileA(); let re_b = await readFileB(); let re_c = await readFileC(); console.log({re_a, re_b, re_c}); } catch (err) { console.log(err); } })()
return:
{ re_a: '111', re_b: '222', re_c: '333' }
上面的例子還能夠這樣寫:
async function test() { try { let re_a = await readFileA(); let re_b = await readFileB(); let re_c = await readFileC(); console.log({re_a, re_b, re_c}); //若是都成功,return: { re_a: '111', re_b: '222', re_c: '333' } } catch (err) { console.log(err); // 若是b失敗,return: b fail } } test().then(function(data){ console.log("success"); },function(err){ console.log("fail"); });
return:
{ re_a: '111', re_b: '222', re_c: '333' } success
四、見上例,實際上 async 申明的 function 返回的就是一個 Promise 對象,這就是 await 必須用在 async 函數中的緣由。async 函數調用不會形成阻塞,它內部全部的阻塞都被封裝在一個 Promise 對象中異步執行。
區別是,async 申明的 function 裏能夠經過 return值 / 拋異常 來實現普通 Promise 的 resolve() / reject()
下面是對等關係:
// async 函數 async function foo () { return 'a' } // Promise function foo () { return Promise.resolve('a') }
// async 函數 async function foo () { throw new Error('error') } // Promise function foo () { return Promise.reject(new Error('error')) }
promise.all
實現需求B
async/await 一樣適用於 Promise.all
,由於 Promise.all 自己返回的就是 promise 對象。
let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("111"); } else { reject("a fail"); } }); } let readFileB = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("222"); } else { reject("b fail"); } }); } let readFileC = function () { return new Promise(function (resolve, reject) { if (read(1)) { resolve("333"); } else { reject("c fail"); } }); } async function test() { try { let re_a = await readFileA(); let re_b = await readFileB(); let re_c = await readFileC(); console.log({re_a, re_b, re_c}); } catch (err) { console.log(err); } } async function test() { try { let results = await Promise.all([ readFileA(), readFileB(), readFileC(), ]); console.log(results); } catch (err) { console.log(err); } } test();
#### 參考資料
[使用 promises]
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises
[理解 JavaScript 的 async/await]
http://www.javashuo.com/article/p-spqrnyuu-km.html
[javascript.info-Async/await]
https://javascript.info/async-await#async-functions