Promise(非同步處理)
阿新 • • 發佈:2021-02-14
技術標籤:Javascripthtml5javascripttypescript前端vue.js
為什麼使用Promise?
解決 更好的解決回撥問題,減少巢狀
Promise 必須為以下三種狀態之一:
等待態(Pending)、執行態(Fulfilled)和拒絕態(Rejected)。
一旦Promise 被 resolve 或 reject,不能再遷移至其他任何狀態(即狀態 immutable)。
基本過程:
1. 初始化 Promise 狀態(pending)
2. 立即執行 Promise 中傳入的 fn 函式,將Promise 內部 resolve、reject 函式作為引數傳遞給 fn ,按事件機制時機處理
3. 執行 then(..) 註冊回撥處理陣列(then 方法可被同一個 promise 呼叫多次)
4. Promise裡的關鍵是要保證,then方法傳入的引數 onFulfilled 和 onRejected,必須在then方法被呼叫的那一輪事件迴圈之後的新執行棧中執行。
// Promise
new Promise(
function (resolve, reject) {
resolve('成功')
reject('失敗')
}
);
缺點 promise一旦建立,無法取消 .then方法每次呼叫都會建立一個新的promise物件,一定程度上造成了記憶體的浪費
// Promise.all([]) 引數是個陣列 非同步操作的並行執行的,所有都執行完成後,才會走then方法
// 誰跑的慢,以誰為準執行回撥,如果其中一個出錯,會立即終止,執行.catch
// 併發執行
Promise.all([async1(), async2(), async3()]).then(function (results) {
console.log(results);
});
// Promise.race([])
// 誰跑的快,以誰為準執行回撥
Promise.race([requestImg(), timeout()]).then(function (results) {
console.log(results);
}).catch(function (reason) {
console.log(reason);
});
Promise.all() 和 romise.race() 都具有短路特性
// Promise.allSettled([])
除錯
Promise 有兩個問題:
不能在返回表示式的箭頭函式中設定斷點;
在 then 程式碼塊中設定斷點,偵錯程式不會跳到下一個 then,因為它只會跳過非同步程式碼;
// 在 .then 裡面 return 一個 Promise
.then(r => {
return serverStatusPromise(r); // 返回 { statusCode: 200 } 的 Promise
})
.then(resp => {
console.log(resp.statusCode); // 200; 注意自動解析的 promise
})
// 每次執行 .then 的時候都會自動建立一個新的 Promise
// 所以promise可以鏈式呼叫
var statusProm = fetchServerStatus();
var promA = statusProm.then(r => (r.statusCode === 200 ? "good" : "bad"));
var promB = promA.then(r => (r === "good" ? "ALL OK" : "NOTOK"));
var promC = statusProm.then(r => fetchThisAnotherThing());
// 對呼叫者來說,Promise 的 resolved/rejected 狀態是唯一的,呼叫者,只可能接收到一種狀態
// Promise 建構函式不是解決方案
// 錯誤
return new Promise((res, rej) => {
fs.readFile("/etc/passwd", function(err, data) {
if (err) return rej(err);
return res(data);
});
});
// Promise 建構函式 只是想要把回撥轉換成 Promise 時使用。
// 正確
return fs.readFile("/etc/passwd", function(err, data) {
if (err) return err;
return data;
});
// 用 Promise 建構函式 封裝 Promise 是多餘的,並且違背了 Promise 本身的目的。
// 使用 Promise.resolve
var similarProm = new Promise(res => res(5));
// ^^ 等價於
var prom = Promise.resolve(5);
// 當不確定它是一個 Promise 還是一個普通的值的時候,可以做一個安全的封裝。
function goodProm(maybePromise) {
return Promise.resolve(maybePromise);
}
goodProm(5).then(console.log); // 5
var sixPromise = fetchMeNumber(6);
goodProm(sixPromise).then(console.log); // 6
goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, 注意,它會自動解析所有的 Promise!
// 使用 Promise.reject
var rejProm = new Promise((res, reject) => reject(5));
rejProm.catch(e => console.log(e)) // 5
// 提前使用 Promise.reject 拒絕執行
function foo(myVal) {
if (!mVal) {
return Promise.reject(new Error('myVal is required'))
}
return new Promise((res, rej) => {
// 從你的大回調到 Promise 的轉換!
})
}
// 不要害怕 reject,也不要在每個 .then 後面加冗餘的 .catch
// 解決 reject
.then(() => 5.length) // <-- 這裡會報錯
.catch(e => {
return 5; // <-- 重新使方法正常執行
})
.then(r => {
console.log(r); // 5
})
.catch(e => {
console.error(e); // 這個方法永遠不會被呼叫 :)
})
// 拒絕一個 reject
.then(() => 5.length) // <-- 這裡會報錯
.catch(e => {
errorLogger(e); // 做一些錯誤處理
return Promise.reject(e); // 拒絕它,是的,你可以這麼做!
})
.then(r => {
console.log(r); // 這個 .then (或者任何後面的 .then) 將永遠不會被呼叫,因為我們在上面使用了 reject :)
})
.catch(e => {
console.error(e); //<-- 它變成了這個 catch 方法的問題
})
// .then(data,err) 和 .then(data).catch(err) 的區別
.then(function() {
return Promise.reject(new Error('something wrong happened'));
}).catch(function(e) {
console.error(e); // something wrong happened
});
.then(function() {
return Promise.reject(new Error('something wrong happened'));
}, function(e) { // 這個回撥處理來自當前 `.then` 方法之前的錯誤
console.error(e); // 沒有錯誤被打印出來
});
let promise = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'promise');
});
promise.then((value) => {
console.log(value); //promise
throw '丟擲一個異常' //通過throw 丟擲
}).catch((e) => {
console.log(e) //輸出: 丟擲一個異常
})
// 避免.then回撥地獄
使用Async/Await
.then(myVal => {
const promA = foo(myVal);
const promB = anotherPromMake(myVal);
return Promise.all([prom, anotherProm])
})
.then(([valA, valB]) => { // 很好的使用 ES6 解構
console.log(valA, valB) // 所有解析後的值
return hungryFunc(valA, valB)
})