callback vs async.js vs promise vs async / await

來源:互聯網
上載者:User

需求:

A、依次讀取 A|B|C 三個檔案,如果有失敗,則立即終止。
B、同時讀取 A|B|C 三個檔案,如果有失敗,則立即終止。

一、callback

需求A

  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:

讀取 a.txt 成功!內容:111讀取 b.txt 成功!內容:222讀取 c.txt 成功!內容:333

需求B:太噁心了,不寫了,總之很繁瑣.

二、async.js

async.js 庫的詳細介紹可以見:[待寫]

需求A

async.series

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() 讀取失敗的話:

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 比的好處:

1、代碼量少了,解決了回調地獄金字塔的缺陷

2、async 的第二個參數回呼函數裡,可以統一處理錯誤(建議用不同的 Error 類作區分)

3、成功返回的結果 datas 可以匯總到一個數組中方便處理

三、promise

[拓展]

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 函數會返回一個新的 promisenew 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_1success_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 函數如果 successCallbackfailureCallback 都為 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 函數會返回一個新的 promisenew 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_1success_2Error: 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 比的好處:

1、代碼量少了,解決了回調地獄金字塔的缺陷

2、.catch 可以統一處理錯誤(建議用不同的 Error 類作區分)

四、async / await 需求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 比的好處:

1、代碼量最少,解決了回調地獄金字塔的缺陷(Promise 通過 then 鏈來解決 callback 多層回調金字塔的問題,現在又用 async/await 來進一步最佳化它)(基於 promise 的 async / await 也試圖淘汰 promise)

2、.catch 可以統一處理錯誤(建議用不同的 Error 類作區分)

[拓展]

1、async 函數就是 Generator 函數的文法糖,本質上並不是同步代碼

2、async 用於申明一個 function 是非同步,而 await (async wait) 用於等待一個非同步方法呼叫執行完成。

3、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

4、見上例,實際上 async 申明的 function 返回的就是一個 Promise 對象,這就是 await 必須用在 async 函數中的原因。async 函數調用不會造成阻塞,它內部所有的阻塞都被封裝在一個 Promise 對象中非同步執行。

區別是,async 申明的 function 裡可以通過 return值 / 拋異常 來實現普通 Promise 的 resolve() / reject()

下面是對等關係:

// async 函數async function foo () {  return 'a'}// Promisefunction foo () {  return Promise.resolve('a')}
// async 函數async function foo () {  throw new Error('error')}// Promisefunction 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]
developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

[理解 JavaScript 的 async/await]
1190000007535316

[javascript.info-Async/await]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.