小浪哥狂聊Promise

小浪哥發表於2018-05-15

小浪哥狂聊Promise

我雙手敲著鍵盤,就無法抱著你,雙手放開鍵盤,就無法養你~~~~~,誒,還是繼續寫文章吧!

本文主要是簡單介紹下Promise及使用,如有錯誤,歡迎指正,這廂有禮了!

什麼是Promise

Promise是ES6新增的一個特徵,它已被列入ES6的正式規範中。

萬物生必有因,Promise的產生有以下三個原因

1. 解決回撥地獄問題(回撥函式多層巢狀,此處不貼程式碼了)。

2. 可以進行鏈式呼叫(通過then,後續詳解then)。

p.then((data)=> {
  console.log(1);
},(err)=> {
   console.log(2);
}).then((data)=>{
    console.log('成功態');
},(err)=>{
    console.log('失敗態');
});
複製程式碼

3. 解決併發非同步,在同一時刻內獲取的併發結果。

let fs = require("fs");
function after(times, callback) { // 可以快取函式,當條件達到時---本函式快取了times的值
    let arr = [];
    return function (data) {
        arr.push(data);
        if (--times === 0) {
            // 當執行兩次後進入本方法
            callback(arr);
        }
    }
}

// 本方法在同一時刻列印出 兩個非同步併發的結果。
let out = after(2,function(arr){
    console.log(arr);
});

fs.readFile('./2.txt','utf8',function(err,data){out(data)});
fs.readFile('./2.txt','utf8',function(err,data){out(data)});
複製程式碼

Promise 的三個狀態

  1. resolved(成功態)
  2. rejected(失敗態)
  3. pending (初始狀態,未完成)

備註: Promise這個名字的英語意思是承諾,表示其他手段無法改變。狀態一旦從pending 狀態 執行為resolved或rejected ,結果就固定了,resolved 和rejected兩種狀態結果不會互相轉換;

配合以下(模擬Promise實現原始碼)程式碼,更容易理解,resolved和rejected兩種狀態不可改變的特性。

function Promise(executor) {//executor 執行器--函式
    let self = this;
    self.status = "pending"; // 等待狀態
    function resolve(value) {  // 成功
        if (self.status == "pending") {// 由pending 狀態轉為resolved
            self.status = "resolved";
        }
    }
    function reject(reason) {// 失敗
        if (self.status == "pending") {// 由pending 狀態轉為rejected
            self.status = "rejected";
    }
    executorz(resolve, reject);
}

例項:
var p = new Promise(function (resolve, reject) {
    if(/* 非同步操作成功 */){
        resolve(); // 此函式執行,代表由 pending 狀態轉為 resolved狀態
    } else {
        reject(); // 此函式執行,代表由pending 狀態轉為 rejected 狀態
    }
});

複製程式碼

Promise 的 then方法

使用案例:

var p = new Promise(function (resolve, reject) {
    if(/* 非同步操作成功 */){
        resolve(value); 
    } else {
        reject(err); 
    }
});
// p.then(onFulfilled,onRejected)   onFulfilled引數代表成功態,onRejected代表失敗態。
p.then((value)=>{
    // 成功態,value是上面resolve傳入的值,可能是普通值(如:1,"34"),可能是Promise物件;如果vaule是普通值,則會作為下一次then的onRejected;如果是Promise物件,需要等待返回的promise執行後的結果傳遞給下一次then的onFulfilled或onRejected。
 
}, (err)=> {
    // 失敗態,err是上面reject傳入的值
   
}).then((value)=>{
    // 如果第一個then 返回的是普通值(不管是成功態返回的,還是失敗態返回的)都會進人當前then的成態裡,
},(err)=>{
       
});
複製程式碼

備註:

  1. then方法返回的是一個新的Promise例項(注意,不是原來那個Promise例項)。因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。
  2. 上面的程式碼使用then方法,依次指定了兩個回撥函式。第一個回撥函式完成以後,會將返回結果作為引數,傳入第二個回撥函式。
  3. 上面程式碼中,第一個then方法指定的回撥函式,返回的是另一個Promise物件。這時,第二個then方法指定的回撥函式,就會等待這個新的Promise物件狀態發生變化。如果變為resolved,就呼叫funcA,如果狀態變為rejected,就呼叫funcB。

promise中的值穿透(then方法中可以什麼引數都不傳):

p.then().then().then((data)=>{
 console.log("then的穿透效果");
   // 成功態
},(err)=>{
    // 失敗態
    console.log('err:' + err);
});

複製程式碼

模擬then值穿透原始碼: Promise 的then方法是例項中的方法,所以寫在Promise的prototye上。

Promise.prototype.then = (onFulfilled, onRejected)=> {
    // then方法中的 成功和失敗引數不傳,就給預設值,實現then值穿透的關鍵步驟;
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value)=> {
        return value;
    }
    onRejected = typeof onRejected === "function" ? onRejected :(err)=> {
        throw err;// 只有丟擲錯誤才會走下一個then的失敗函式
        // return err; 不能這麼寫的原因是錯誤函式只要有返回值,就會走下一個then的成功函式。
    }
    
}
複製程式碼

promise 的catch 方法

Promise.prototype.catch 方法是由then(null,rejected)實現,專門用來指定發生錯誤時的回撥函式。

程式碼一:

p.then((data)=>{
    
},(err)=>{
    
}).then(null,(err)=>{
    
})
複製程式碼

程式碼二:

p.then((data)=>{
    
}).catch((err)=>{
    
});
複製程式碼

*** 程式碼一和程式碼二的效果一樣,實質也一樣。

原始碼實現catch方法:

Promise.prototype.catch=(onRejected)=>{
    // catch 接收的引數只用於報錯。
    return this.then(null, onRejected);
};
複製程式碼

Promise 的 all方法

例項:

Promise.all([read("1.txt"), read('2.txt')]).then(([one, two]) => { 
// 此處返回的是一個陣列,因為all的引數的是一個陣列,所以返回值也是一個陣列====此處[one,two]用到了物件解構
    console.log(one, two)
});
複製程式碼

備註:

  1. Promise.all 方法的引數是由Promise物件組成的陣列,執行後返回的依舊是promise陣列。
  2. all方法,多個任務全部成功才算是成功,如果有一個失敗了就算是失敗。
  3. all屬於promise的靜態方法,所以直接放到Promise類中。

配合以下(模擬all實現原始碼)程式碼,更容易理解。

Promise.all = function (promises) {
    return new Promise((resolve,reject)=>{
      let arr = [];
      let i = 0; // i的目的是為了保證獲取全部成功,來設定的索引
      function processData(index,data) {
        arr[index] = data;
        i++;
        if (i === promises.length){
          resolve(arr);
        }
      }
      for(let i = 0;i<promises.length;i++){
        promises[i].then(data=>{
          processData(i,data); // 此處使用了遞迴
        }, reject);
      }
    })
  }
複製程式碼

Promise 的 race 方法

例項:

Promise.race([read("1.txt"), read('2.txt')]).then(data => {
    console.log(data);
}).catch(err => {
    console.log(err);
});
複製程式碼

備註:

  1. Promise.race方法的引數是由Promise物件組成的陣列,race翻譯為中文是‘賽跑’,誰最先有結果就返回誰的結果。 如果有一項任務先成功了,那就成功,如果有一項任務失敗了,那就是失敗。
  2. 返回的是布林值。
  3. race屬於promise的靜態方法,所以直接放到Promise類中。

配合以下(模擬race實現原始碼)程式碼,更容易理解。

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject);// promise 陣列中的promise物件執行,誰先resolve或者reject(有結果,不管成功或失敗)Promise.all 就會返回一個以此結果為準的promise物件。
    }
  });
}
複製程式碼

如果有錯誤,歡迎指正,時間不早了,回家做飯給媳婦賠罪去了。

相關文章