1. 程式人生 > 其它 >Promise(非同步處理)

Promise(非同步處理)

技術標籤: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)
})