《JS異步編程之 callback》一文咱們瞭解了「JS 是基於單線程事件循環」的概念構建的,回調函數不會當即執行,由事件輪詢去檢測事件是否執行完畢,當執行完有結果後,將結果放入回調函數的參數中,而後將回調函數添加到事件隊列中等待被執行。javascript
同時也講了回調函數的問題:java
一是「回調地獄」,由於異步回調函數的特色:回調函數是做爲異步函數的參數,一層一層嵌套,當嵌套過多,將使代碼邏輯變得混亂,也沒法作好錯誤捕捉和處理(只能在回調函數內部 try catch)。node
二是回調的執行方式不符合天然語言的線性思惟方式,不容易被理解。ajax
三是控制反轉(控制權在其餘人的代碼上),假如異步函數是別人提供的庫,咱們把回調函數傳進去,咱們並不能知道異步函數在調用回調函數以外作了什麼事情。編程
func1(() => {
func2(() => {
func3(() => {
func4(() => {
try {
...
} catch (err){
...
}
})
});
});
});
複製代碼
首先,Promise 中文翻譯爲「承諾」, 是 JavaScript 的一種對象,表示承諾終將返回一個結果,不管成功仍是失敗。json
Promise 有三個狀態:等待中(pending),完成(fullfilled),失敗(rejected), Promise 的設計具備原子性,狀態一旦從 pending 狀態轉換爲 fullfilled 狀態或者 rejected 狀態後,將不能被改變。promise
var promise1 = new Promise((resolve, reject) => {
console.log("Promise 構造器會當即執行");
setTimeout(function (){
if(true) {
resolve("完成");
} else {
reject("失敗");
}
}, 1000);
})
promise1
.then((result) => {
// do something
console.log(result);
return 1
// return Promise.resolve(1); // 返回一個決議爲成功的 promise 實例
// return Promise.reject("error"); // 返回一個決議爲拒絕的 Promise 實例
})
.then((result) => {
// .then() 方法會返回一個 promise, 完成調用的參數爲前一個 promise 的返回值或者決議值。
// do other things
console.log(result);
throw new Error("錯誤") // 拋出錯誤是隱式拒絕
})
.catch((error) => {
// 捕捉錯誤
console.log(error)
})
.then(() => {
// 還能繼續執行!
})
.finally(() => {
// always do somethings
console.log("finally!")
})
複製代碼
鏈式調用
Promise 使用 then 方法後還會返回一個新的 Promise 對象,便於咱們傳遞狀態數據,同時鏈式寫法接近於同步寫法,更符合線性思惟。閉包
錯誤捕捉
相比回調函數的錯誤沒法在外部捕捉的問題,Promise 可以爲一連串的異步調用提供錯誤處理。異步
控制反轉再反轉
因爲第三方提供的異步函數,沒法保證回調函數如何被執行,可是 Promise 的特色,可以保證異步函數只能被 resolve 一次,以及始終以異步的形式執行代碼。異步編程
能夠利用 Promise.all 和 Promise.race 來解決 Promise 始終未決議和並行 Promise 嵌套的問題
每一個 .then() 都是一個獨立的做用域
加入有不少個 .then() 方法,就會建立不少個獨立的做用域,那麼將只能經過外面包裹一層函數做用域的閉包來共享狀態數據
沒法取消單個 .then()
當 Promise 鏈中任意一個 .then() 方法中有語句執行錯誤後,儘管通過 catch 方法的錯誤處理,仍是並不會中斷整個 Promise 鏈的執行。
沒法得知進度
因爲 Promise 只能從 pending 到 fullfilled 或 rejected 狀態,沒法得知 pending 階段的進度。
// Promise 封裝 ajax
function fetch(method, url, data){
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
var method = method || "GET";
var data = data || null;
xhr.open(method, url, true);
xhr.onreadystatechange = function() {
if(xhr.status === 200 && xhr.readyState === 4){
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
xhr.send(data);
})
}
// 使用
fetch("GET", "/some/url.json", null)
.then(result => {
console.log(result);
})
// 封裝 nodejs error first 風格回調
function readFile(url) {
return new Promise((resolve, reject) => {
fs.readFile(url,'utf8', (err, data) => {
if(err) {
reject(err);
return;
}
resolve(data)
})
})
}
複製代碼
Promise 是 ES6 提出的簡化異步流程控制新規範,強調異步任務的完成狀態且具備原子性,這使得咱們的代碼更容易追蹤和維護。Promise 在事件輪詢中屬於異步事件隊列中的微任務,而微任務老是一次性所有執行,而宏任務是每輪輪詢執行一個,此節內容參考我以前的文章《JS專題之事件循環》。
2019/02/24 @Manncoffee