學(xué)習(xí)Promise

一饲宛、Promise

1.異步任務(wù)的處理

在ES6出來之后扮匠,有很多關(guān)于Promise的講解、文章芙贫,也有很多經(jīng)典的書籍講解Promise.

  • 雖然等你學(xué)會了Promise之后搂鲫,會覺得Promise不過如此傍药,但是在初次接觸的時候都會覺得這個東西不好理解。

那么這里我從幾個實際的例子來作為切入點:

  • 我們調(diào)用一個函數(shù)魂仍,這個函數(shù)中發(fā)送網(wǎng)絡(luò)請求(我們可以用定時器來模擬)
  • 如果發(fā)送網(wǎng)絡(luò)請求成功了拐辽,那么告知調(diào)用者發(fā)送成功,并且將相關(guān)數(shù)據(jù)返回過去擦酌。
  • 如果發(fā)送網(wǎng)絡(luò)請求失敗了俱诸,那么告知調(diào)用者發(fā)送失敗,并且告知錯誤信息赊舶。

【任何一個新技術(shù)的出來睁搭,就是用來解決原有技術(shù)的某一個痛點】

//request.js
function requestData(url){
  // 模擬網(wǎng)絡(luò)請求
  setTimeout(()=>{
    //拿到請求的1結(jié)果
    //url傳入的是 coderwhy,請求成功
    if(url==="coderwhy"){
        let names=["wjy","hyz","tqy"];

        return names;//* 但其實這個return外面是拿不到結(jié)果的
    }else{// 否則請求失敗
      let errMessage="請求失敗,url錯誤";

      return errMessage;//* 但其實這個return外面是拿不到結(jié)果的
    }
    
  },3000)
}

// main.js
requestData("coderwhy")

修改requestData參數(shù)笼平,需要傳入成功的回調(diào)函數(shù)successCallback和失敗的回調(diào)函數(shù)failCallback园骆。

//request.js
function requestData(url,successCallback,failCallback){
  // 模擬網(wǎng)絡(luò)請求
  setTimeout(()=>{
    //拿到請求的1結(jié)果
    //url傳入的是 coderwhy,請求成功
    if(url==="coderwhy"){
        let names=["wjy","hyz","tqy"];
        successCallback(names);
    }else{// 否則請求失敗
      let errMessage="請求失敗,url錯誤";
      failCallback(errMessage)
    }
    
  },3000)
}

// main.js
requestData("coderwhy",(res)=>{
  console.log("成功:",res);
},(err)=>{
  console.log("失斣⒌鳌:",err);
})

1.1 回調(diào)函數(shù)的弊端

  • 如果是我們自己封裝的requestData锌唾,那么我們在封裝的時候必須要自己設(shè)計好callback名稱,并且使用好
  • 如果我們使用的是別人封裝的requestData或者一些第三方庫夺英,那么我們必須去看別人的源碼或者文檔晌涕,才知道這個函數(shù)怎么去獲取結(jié)果的。
  • 這種溝通成本非常多的

那么有沒有一種更好的方式:

  • 那么有沒有會返回一個承諾痛悯,這個承諾(規(guī)范了所有的代碼編寫邏輯)
// *  更好的方案 承諾(規(guī)范好了所有的代碼編寫邏輯)
function requestData2(){
  return "承諾"
}

const chengnuo=requestData2()

2.什么是Promise

Promise是ES6新增的一個API余黎,它是一個類,可以翻譯成 承諾载萌、許諾惧财、期約亲族;

當(dāng)我們需要給予調(diào)用者一個承諾:待會兒我會傳給你回調(diào)數(shù)據(jù)時,就可以創(chuàng)建一個Promise對象

在通過new 創(chuàng)建Promise對象時可缚,我們需要傳入一個回調(diào)函數(shù)霎迫,我們稱之為executor

  • 這個回調(diào)函數(shù)會被立即執(zhí)行,并且給傳入另外兩個回調(diào)函數(shù)resolve帘靡、reject
  • 當(dāng)我們調(diào)用resolve回調(diào)函數(shù)時知给,會執(zhí)行Promise對象的then方法傳入的回調(diào)函數(shù)
  • 當(dāng)我們調(diào)用reject回調(diào)函數(shù)時,會執(zhí)行Promise對象的catch方法傳入的回調(diào)函數(shù)描姚。
image-20220514142507394.png
function foo(){
  // Promise
  return new Promise((resolve,reject)=>{
    // resolve();
    reject();
  })
}

// main.js
const fooPromise=foo();

// * then方法傳入的回調(diào)函數(shù)涩赢,會在Promise執(zhí)行resolve函數(shù)時,會回調(diào)轩勘。
fooPromise.then(()=>{

})

// * catch方法傳入的回調(diào)函數(shù)筒扒,會在Promise執(zhí)行reject函數(shù)時,被回調(diào)
fooPromise.catch(()=>{

})

// class Person{
//   constructor(callback){
//     function foo(){

//     }
//     function bar(){

//     }
//     callback(foo,bar)
//   }
// }

// const p=new Person((foo,bar)=>{
//   foo();
//   bar()
// });

// * Promise是一個類绊寻,可以傳入?yún)?shù)花墩,參數(shù)是回調(diào)函數(shù),這個回調(diào)函數(shù)在傳入時澄步,會立即被執(zhí)行冰蘑。
/**
 * * 傳入的回調(diào)函數(shù)可以稱之為 executor
 * * 這個回調(diào)函數(shù)會有兩個參數(shù):
 * * resolve :成功時,執(zhí)行resolve回調(diào)
 * * reject:失敗時村缸,執(zhí)行reject回調(diào)
 */
const promise=new Promise((resolve,reject)=>{
  // console.log("promise傳入的函數(shù)立即被執(zhí)行了");
  resolve();
})

promise.then(()=>{

})

promise.catch(()=>{
  
})
image-20220514144055335.png

2.1 異步處理的Promise方式

/**
 * * 回調(diào)的弊端
 * *   1.如果是我們自己封裝的requestData祠肥,那么我們在封裝的時候必須要自己設(shè)計好callback名稱,并且使用好
* *    2.如果我們使用的是別人封裝的requestData或者一些第三方庫梯皿,那么我們必須去看別人的源碼或者文檔仇箱,才知道這個函數(shù)怎么去獲取結(jié)果的。
* *    3.這種溝通成本非常多的
 */

//request.js
function requestData(url,successCallback,failCallback){
  // 模擬網(wǎng)絡(luò)請求
  
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      //拿到請求的1結(jié)果
      //url傳入的是 coderwhy东羹,請求成功
      if(url==="coderwhy"){
          let names=["wjy","hyz","tqy"];
          resolve(names);
      }else{// 否則請求失敗
        let errMessage="請求失敗,url錯誤";
        reject(errMessage)
      }
      
    },3000);
  })
}



//main.js

const promise=requestData("coderwhy");

// * then方法傳入兩個回調(diào)函數(shù)
// * 第一個回調(diào)函數(shù)剂桥,會在Promise執(zhí)行resolve函數(shù)時,被回調(diào)
// * 第二個回調(diào)函數(shù)百姓,會在Promise執(zhí)行reject函數(shù)時渊额,被回調(diào)
promise.then((res)=>{
  console.log("請求成功:",res);
},(err)=>{
  console.log("請求失敗了:",err);
})

3.Promise的三種狀態(tài)

// 完全等價于下面的代碼
new Promise((resolve,reject)=>{
  console.log("------------------");//* 階段:pending 
  resolve();
}).then(res=>{
  console.log("res:",res);//* fulfilled(固定、已敲定)
},err=>{
  console.log("err:",err);//* rejected (已拒絕 )
})

上面的Promise使用過程垒拢,我們可以將它劃分成三個狀態(tài):

  • 待定(pending):初始狀態(tài)旬迹,既沒有被兌現(xiàn),也沒有被拒絕

    • 當(dāng)執(zhí)行executor中的代碼時求类,處于該狀態(tài)奔垦。
  • 已兌現(xiàn)(fulfilled):意味著操作成功完成

    • 執(zhí)行了resolved時,處于該狀態(tài)
  • 已拒絕(rejected):意味著操作失敗

    • 執(zhí)行了reject 時尸疆,處于該狀態(tài)

Promise狀態(tài)一旦被確定下來椿猎,那么就是不可更改的(鎖定)惶岭,例如下面我們先執(zhí)行了resolve,將狀態(tài)改為 已兌現(xiàn)(fulfilled)犯眠。后面又執(zhí)行了reject按灶,企圖將狀態(tài)修改為已拒絕(rejected),但發(fā)現(xiàn)其實并不能進(jìn)行修改的筐咧。

// * Promise狀態(tài)一旦被確定下來鸯旁,那么就是不可更改的(鎖定)
new Promise((resolve,reject)=>{
  console.log("------------------");//* 階段:pending 
  resolve();//* fulfilled(固定、已敲定)
  reject();////* rejected (已拒絕 ) 量蕊,這行代碼沒有任何意義铺罢。
  // resolve(123)
}).then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
})

4. resolve參數(shù)

  • 情況一:如果resolve傳入一個普通的值或者對象,那么這個值就會作為then回調(diào)的參數(shù)
  • 情況二:如果resolve傳入的是另外一個Promise残炮,那么這個新Promise的狀態(tài)會決定原Promise的狀態(tài)
  • 情況三:如果resolve傳入的是一個對象韭赘,并且這個對象有實現(xiàn)then方法,那么就會執(zhí)行then方法势就,并且根據(jù)then方法的結(jié)果來決定Promise的狀態(tài)泉瞻。
/**
 * resolve(參數(shù))
 * *  (1)普通的值或者對象
 * *  (2)傳入一個Promise
 * *    那么當(dāng)前的Promise狀態(tài)由傳入的Promise來決定
 * *    相當(dāng)于狀態(tài)進(jìn)行了移交
 * *  (3)傳入一個對象,并且這個對象有實現(xiàn)的then方法 (thenable)蛋勺,那么也會執(zhí)行該then方法瓦灶,并且由該then方法決定后續(xù)狀態(tài)
 */

// * 傳入Promise的特殊情況
// const newPromise=new Promise((resolve,reject)=>{
//   // resolve("aaaaa")
//   reject("err message")
// })
// new Promise((resolve,reject)=>{
//   resolve(newPromise)
// }).then(res=>{
//   console.log("res:",res);
// },err=>{
//   console.log("err:",err);
// })



//*  2.傳入一個對象鸠删,這個對象有實現(xiàn)then方法抱完,由該then方法決定后續(xù)狀態(tài)
new Promise((resolve,reject)=>{

  const obj={
    then:function(resolve,reject){
      // resolve("resolve message");

      reject("reject message")
    }
  }
  resolve(obj);
}).then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
}) 

5.then方法

5.1 then方法-接受兩個參數(shù)

then方法是Promise對象的一個方法,它其實是 放在Promise的原型上的Promise.prototype.then

then方法接受兩個參數(shù):

  • fulfilled的回調(diào)函數(shù):當(dāng)狀態(tài)變成fulfilled時會回調(diào)的函數(shù)
  • rejected的回調(diào)函數(shù):當(dāng)狀態(tài)變成rejected時會回調(diào)的函數(shù)
promise.then(res=>{
  console.log("res:",res);
},err=>{
 console.log("err:",err); 
})

// 等價于下面
promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})

5.2 then方法-可以多次調(diào)用

同一個promise對象的then方法可以調(diào)用多次刃泡。

當(dāng)resolve方法被回調(diào)時巧娱,所有傳入到then方法的回調(diào)函數(shù)會執(zhí)行。

// * Promise有哪些對象方法
// console.log(Promise.prototype);
// console.log(Object.getOwnPropertyDescriptors(Promise.prototype));

const promise=new Promise((resolve,reject)=>{
  resolve("hahahaha")
})

// *同一個promise可以多次調(diào)用then方法
// * 當(dāng)我們的resolve方法被回調(diào)時烘贴,所有的then方法傳入的回調(diào)函數(shù)都會被調(diào)用
promise.then((res)=>{
  console.log("res:",res);
})


promise.then((res)=>{
  console.log("res2:",res);
})

promise.then((res)=>{
  console.log("res3:",res);
})

5.3 then方法——返回值

其實then方法有返回值禁添,返回的是新的Promise對象

then方法的回調(diào)函數(shù)也有返回值,當(dāng)then方法中的回調(diào)函數(shù)返回一個結(jié)果時桨踪,那么它處于fulfilled狀態(tài)老翘,并且會將結(jié)果作為resolve的參數(shù)

  • 如果返回的是一個普通的值(數(shù)字、字符串锻离、對象)铺峭,這個值會作為then方法返回的Promise對象的resolve的值

    • 如果沒有返回值,則默認(rèn)返回的是undefined

      const promise=new Promise((resolve,reject)=>{
        resolve("hahahaha")
      })
      promise.then(res=>{
        // *如果回調(diào)函數(shù)沒有返回值汽纠,則返回的是一個undefined
        return {name:"wjy"}
      }).then(res=>{
        console.log("res:",res);
      })
      
      
  • 如果返回的是一個Promise對象卫键,

    • 這個返回的Promise對象會決定then方法返回的Promise的狀態(tài)
    const promise=new Promise((resolve,reject)=>{
      resolve("hahahaha")
    })
    // *如果返回的是一個Promise對象
    promise.then(res=>{
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{
          resolve("hello wjy")
        },3000)
      })
    }).then(res=>{
      console.log("res:",res);
    })
    
    
  • 如果返回的是一個對象,這個對象實現(xiàn)了thenable(then方法)虱朵,會立即執(zhí)行then方法莉炉,并根據(jù)then方法的結(jié)果來決定Promise的狀態(tài)钓账。
// *如果返回了是一個對象,并且這個對象上
promise.then(res=>{  
  return {  //* new Promise(resolve=>resolve(obj.then))
    then:function(resolve,reject){
      resolve(2222)
    }
  } 
}).then(res=>{
  console.log("res:",res);
})

6.catch方法

then方法內(nèi)部可以傳入兩個參數(shù)(兩個回調(diào)函數(shù))

  • 當(dāng)resolve函數(shù)被執(zhí)行時絮宁,會調(diào)用then方法的第一個參數(shù)(回調(diào)函數(shù))

  • 當(dāng)reject函數(shù)被執(zhí)行時梆暮,會調(diào)用then方法的第二個參數(shù)(回調(diào)函數(shù))【或者內(nèi)部發(fā)生錯誤,也會被then方法的第二個參數(shù)捕獲到绍昂√杼悖】

    • const promise=new Promise((resolve,reject)=>{
        reject("rejected status")
      })
      
      promise.then(undefined,err=>{
        console.log("err:",err);
      })
      
    • const promise=new Promise((resolve,reject)=>{
        throw new Error("rejected status")
      })
      
      promise.then(undefined,err=>{
        console.log("err:",err);
      })
      

如果將成功和失敗的回調(diào)函數(shù)寫在一塊,閱讀性差治专,比如下面:

const promise=new Promise((resolve,reject)=>{
  // reject("rejected status")
  throw new Error("rejected status")
})

promise.then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
})

支持另外一種方法:

通過catch方法傳入錯誤或拒絕的捕獲的回調(diào)函數(shù):

const promise=new Promise((resolve,reject)=>{
  reject("rejected status")
})


// * 通過catch方法傳入錯誤或(拒絕)捕獲的回調(diào)函數(shù)
// promise/a+規(guī)范
promise.catch(err=>{
  console.log("err:",err)
})

還支持另外一種寫法:

  • catch會優(yōu)先捕獲第一個Promise對象的異常捕獲卖陵,如果第一個Promise對象沒有發(fā)生異常,則不會使用张峰,會留著下一次使用泪蔫。如果第二個Promise對象發(fā)生異常,這個catch會去捕獲喘批。
  • 這個catch方法比較特殊
const promise=new Promise((resolve,reject)=>{
  reject("rejected status")
  // throw new Error("rejected status")
})


promise.then(res=>{
  return 111
}).catch(err=>{
     console.log("err:",err)
})

image-20220514193022157.png
const promise=new Promise((resolve,reject)=>{
  resolve("resolve status")
})


promise.then(res=>{
  throw new Error("發(fā)生錯誤")
  return 111
}).catch(err=>{
  console.log("err:",err);//*如果第一個promise對象沒有異常撩荣,所以就不會執(zhí)行,等著下一次使用,會給下一個Promise對象使用
  console.log("------");
})
image-20220514193638998.png

6.1 拒絕捕獲的問題

// * 拒絕捕獲的問題(前面課程)
const promise=new Promise((resolve,reject)=>{
  reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
  console.log("res:",res);
})

promise.catch(err=>{
  console.log("err:",err);
})

這個代碼運行后會報錯的饶深。

image-20220514195146223.png
  • 之間不會相互影響的
  • 因為在then方法中沒有進(jìn)行捕獲餐曹,以異常的方式退出。
  • 如果代碼中沒有then方法就不會報錯

改成下面這種就不會報錯:

// * 拒絕捕獲的問題(前面課程)
const promise=new Promise((resolve,reject)=>{
  reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})

const promise=new Promise((resolve,reject)=>{
  reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
})

6.2 reject和throw同時存在拋出哪個異常

如果執(zhí)行了reject()敌厘,throw會被忽略

如果執(zhí)行了throw台猴,throw后面的代碼都不會再執(zhí)行。

6.3 catch方法多次調(diào)用

catch方法也是Promise對象上的一個方法俱两,它也是放在Promise的原型上的Promise.prototype.catch方法饱狂。

同一個Promise對象的catch方法可以被調(diào)用多次

  • 每當(dāng)調(diào)用我們可以傳入對應(yīng)的reject回調(diào)
  • 當(dāng)Promise的狀態(tài)變成rejected的時候,這些回調(diào)函數(shù)都會被執(zhí)行宪彩。
const promise=new Promise((resolve,reject)=>{
  reject("aaa");
})

promise.catch(err=>{
  console.log("err1:",err);
})

promise.catch(err=>{
  console.log("err2:",err);
})

promise.catch(err=>{
  console.log("err3:",err);
})

6.4 catch方法——返回值

事實上catch方法也是會返回一個Promise對象的休讳,所以catch方法后面我們可以繼續(xù)調(diào)用then方法或者catch方法

 const promise=new Promise((resolve,reject)=>{
  reject("aaa");
})

promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
  return "catch return value"
}).then(res=>{
  console.log("res:",res);
})
const promise=new Promise((resolve,reject)=>{
  reject("aaa");
})
promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("my name")
    },3000)
  })
}).then(res=>{
  console.log("res:",res);
})

7.fanally方法

fanally是ES9(ES2018)新增的一個特性:表示無論Promise的狀態(tài)變?yōu)閒ulfilled還是reject狀態(tài),最終都會被執(zhí)行的代碼尿孔。

finally方法是不接收參數(shù)的俊柔,因為無論前面是fulfilled狀態(tài),還是reject狀態(tài)活合,它都會執(zhí)行雏婶。

const promise=new Promise((resolve,reject)=>{
  resolve("resolve message");
})

promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
}).finally(()=>{
  console.log("finally code execute");
})

finally其實也是返回一個Promise對象,但是其實很少人會用它

8.類方法——resolve方法

前面我們學(xué)習(xí)的then芜辕、catch尚骄、finally方法都屬于Promise的實例方法,都是存放在Promise的prototype上的侵续。

下面我們將學(xué)習(xí)類方法

有時候我們已經(jīng)有一個現(xiàn)成的內(nèi)容倔丈,希望將其轉(zhuǎn)成Promise來使用憨闰,這個時候我們可以使用Promise.resolve方法來完成。

  • Promise.resolve()方法:相當(dāng)于new Promise需五,并且執(zhí)行resolve操作鹉动。

resolve的參數(shù)形式:

  • 情況一:參數(shù)是一個普通的值或者對象
  • 情況二:參數(shù)是一個Promise對象
  • 情況三:參數(shù)是一個thenable
function foo(){
  const obj={name:"wjy"}
  return new Promise((resolve)=>{
    resolve(obj);
  });
}

foo().then(res=>{
  console.log("res:",res);
})

等價于下面:

function foo(){
  const obj={name:"wjy"}
 return Promise.resolve(obj);
}

foo().then(res=>{
  console.log("res:",res);
})

8.1 參數(shù)形式

// * 1.傳入普通的值
// const promise=Promise.resolve({name:"wjy"});

// * 2.傳入一個Promise,由這個新的Promise對象決定原Promise對象的狀態(tài)
// const promise=Promise.resolve(new Promise((resolve,reject)=>{
//   // resolve("resolve2 message")
//     reject("reject message")
// }))

// promise.then(res=>{
//   console.log("res:",res);
// }).catch(err=>{
//   console.log("err:",err);
// })

// * 3. 傳入thenable對象(實現(xiàn)了then方法),會立即執(zhí)行then方法宏邮,由then方法的結(jié)果決定其Promise的狀態(tài)
const promise=Promise.resolve({
  then:function(resolve,reject){
    // resolve("resolve ");
    reject("reject message")
  }
})

promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})

9.類方法——reject方法

reject方法類似于resolve方法垫挨,只是會將Promise對象的狀態(tài)設(shè)置為rejected狀態(tài)

Promise.reject的用法相當(dāng)于new Promise乾颁,并且會調(diào)用reject

Promise.reject無論傳過來的參數(shù)是什么狀態(tài)澜汤,都會直接作為reject的參數(shù)傳遞到catch的稠诲。

10.類方法——all方法

它的作用是將多個Promise包裹在一起,形成一個新的Promise

新的Promise狀態(tài)由包裹的所有Promise決定

  • 當(dāng)所有的Promise狀態(tài)變?yōu)閒ulfilled狀態(tài)時飒炎,新的Promise狀態(tài)為fulfilled埋哟,并且會將所有的Promise的返回值組成一個數(shù)組
  • 當(dāng)有一個Promise狀態(tài)變?yōu)閞ejected狀態(tài)時,新的Promise狀態(tài)為rejected郎汪,并且會將第一個reject的返回值作為參數(shù)
const p1=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(1111)
  },1000)
})
const p2=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(22222)
  },2000)
})
const p3=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(33333)
  },3000)
})

// * 需求:所有的Promise都變成fulfilled,再拿到結(jié)果
//*  意外:在拿到所有結(jié)果之前赤赊,有一個promise變成了rejected,那么整個promise是rejected
Promise.all([p1,p2,p3,"aaaa"]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  // * 最后轉(zhuǎn)到catch執(zhí)行
  console.log("err:",err);
})

// * all:所有的promise為fulfilled時才會執(zhí)行then方法,如果有一個狀態(tài)為rejected煞赢,就會轉(zhuǎn)成執(zhí)行catch方法

11.類方法——allSettled方法

all方法有一個缺陷:當(dāng)有一個Promise變成rejected狀態(tài)時抛计,新Promise就會立即變成對應(yīng)的rejected狀態(tài)。

  • 那么對于resolved的照筑,以及仍然處于pending狀態(tài)的Promise吹截,我們是獲取不到對應(yīng)的結(jié)果的。

在ES11(ES2020)中朦肘,添加了新的API Promise.allSettled

  • 該方法會在所有的Promise都有結(jié)果(settled)饭弓,無論是fulfilled,還是rejected時媒抠,才會有最終的狀態(tài)。
  • 并且這個Promise的結(jié)果一定是fulfilled的
const p1=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(1111)
  },1000)
})
const p2=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(22222)
  },2000)
})
const p3=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(33333)
  },3000)
})


// allSettled
// * 新的Promise的狀態(tài)一定是fulfilled
Promise.allSettled([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})

12. 類方法——race

如果有一個Promise有了結(jié)果咏花,我們就希望決定最終新的Promise的狀態(tài)趴生,那么可以使用race方法

  • race是競技、競賽的意思昏翰,表示多個Promise相互競爭苍匆,誰先有結(jié)果,那么就使用誰的結(jié)果棚菊。
const p1=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(1111)
  },1000)
})
const p2=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve(22222)
  },2000)
})
const p3=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(33333)
  },3000)
})


// * race :競技 競賽
Promise.race([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})

13.類方法——any方法

any方法是ES12中新增的方法浸踩,和race方法是類似的:

  • any方法會等到一個fulfilled狀態(tài),才會決定新Promise的狀態(tài)
  • 如果所有的Promise都是rejected的统求,那么也會等到所有的Promise都變成rejected狀態(tài)检碗。
const p1=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(1111)
  },1000)
})
const p2=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(22222)
  },2000)
})
const p3=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(33333)
  },3000)
})


// * any
Promise.any([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  // *可以通過.errors拿到合計的錯誤信息
  console.log("err:",err.errors);
})
  • 如果所有的Promise都是reject的据块,那么會報一個AggregateError的錯誤。

二折剃、手寫Promise

1.Promise的設(shè)計和構(gòu)造方法

const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范另假,要符合PromiseA+
class HYPromise{
  // * 記錄狀態(tài)
  constructor(executor) {
    // * 保存Promise的狀態(tài)
    this.status=PROMISE_STATUS_PENDING;
    // * 保存?zhèn)魅氲闹?    this.value=undefined;
    this.reason=undefined;
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        this.status=PROMISE_STATUS_FULFILLED;
        this.value=value;
        console.log("resolve被調(diào)用");
      }
     
    }
    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        console.log("reject被調(diào)用");
      }
     
    }
    executor(resolve,reject)
  }
}

const promise=new HYPromise((resolve,reject)=>{
  console.log("狀態(tài)pending");
  resolve("1111")
})

2.then方法的設(shè)計

 const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范,要符合PromiseA+
class HYPromise{
  // * 記錄狀態(tài)
  constructor(executor) {
    // * 保存Promise的狀態(tài)
    this.status=PROMISE_STATUS_PENDING;
    // * 保存?zhèn)魅氲闹?    this.value=undefined;
    this.reason=undefined;
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        this.status=PROMISE_STATUS_FULFILLED;
        queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)怕犁,會放在下一次事件循環(huán)時使用
        
          this.value=value;
          console.log("resolve被調(diào)用");
          // * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
          this.onFulfilled(this.value)
        
      })
      }
     
     
    }
    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        this.status=PROMISE_STATUS_REJECTED;
       queueMicrotask(()=>{
        this.reason=reason;
        console.log("reject被調(diào)用");
        // * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
        this.onRejected(this.reason);
       })
      }
     
    }
    executor(resolve,reject)
  }
  then(onFulfilled,onRejected){
    this.onFulfilled=onFulfilled;
    this.onRejected=onRejected;
  }
 
}

const promise=new HYPromise((resolve,reject)=>{
  console.log("狀態(tài)pending");
  // resolve("1111")
  reject("reject message")
})

promise.then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
})

promise.then(res=>{
  console.log("res2:",res);
},err=>{
  console.log("err2:",err);
})

// * 多次調(diào)用then边篮,后面的then方法會覆蓋前面的then方法,而且只會調(diào)用一次方法
// * then方法不能實現(xiàn)鏈?zhǔn)秸{(diào)用

3.then方法的優(yōu)化一

  • 實現(xiàn)then多次調(diào)用
  • 如果Promise狀態(tài)已經(jīng)確定奏甫,再次調(diào)用then方法戈轿,傳入的回調(diào)函數(shù)應(yīng)該直接執(zhí)行
 const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范,要符合PromiseA+
class HYPromise{
  // * 記錄狀態(tài)
  constructor(executor) {
    // * 保存Promise的狀態(tài)
    this.status=PROMISE_STATUS_PENDING;
    // * 保存?zhèn)魅氲闹?    this.value=undefined;
    this.reason=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)阵子,會放在下一次事件循環(huán)時使用
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          console.log("resolve被調(diào)用");
          // * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
         this.onFulfilledFns.forEach(Fn=>{
           Fn(this.value)
         })
        
      })
      }
     
     
    }
    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
       
       queueMicrotask(()=>{ 
         this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        console.log("reject被調(diào)用");
        // * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason)
        })
       })
      }
     
    }
    executor(resolve,reject)
  }
  then(onFulfilled,onRejected){
    // 1.如果then方法調(diào)用的時候凶杖,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
    if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
      onFulfilled(this.value);
    }else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
      onRejected(this.reason)
    }else{
      // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
    this.onFulfilledFns.push(onFulfilled);
    this.onRejectedFns.push(onRejected);
    }
    
  }
 
}

const promise=new HYPromise((resolve,reject)=>{
  console.log("狀態(tài)pending");
  resolve("1111");//resolved/fulfilled
})

promise.then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err:",err);
})

promise.then(res=>{
  console.log("res2:",res);
},err=>{
  console.log("err2:",err);
})



setTimeout(()=>{
  promise.then(res=>{
    console.log(res);
  })
},1000)

// * 解決了then方法的多次調(diào)用
// * 在確定Promise狀態(tài)之后款筑,再次調(diào)用then
// const promise2=new Promise((resolve,reject)=>{
//   resolve("aaaaa");
// })

// setTimeout(()=>{
//   promise2.then(res=>{ //原生編寫了
//     console.log(res);
//   })
// },1000)

4.then方法的優(yōu)化二

  • 實現(xiàn)了一個鏈?zhǔn)秸{(diào)用(返回一個新的Promise對象智蝠,使用新的Promise對象的resolve方法去處理對應(yīng)的回調(diào)函數(shù)的返回值)
  • 實現(xiàn)了對executor執(zhí)行時發(fā)生異常(錯誤),應(yīng)該執(zhí)行reject函數(shù)
  • 后面封裝了一個工具函數(shù)奈梳,用來處理異常和回調(diào)
 const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    let result=exeFn(value);
    reject(result);
  }catch(err){
    reject(err);
  }
}
// * 自己手寫的Promise要符合的一定的規(guī)范杈湾,要符合PromiseA+
class HYPromise{
  // * 記錄狀態(tài)
  constructor(executor) {
    // * 保存Promise的狀態(tài)
    this.status=PROMISE_STATUS_PENDING;
    // * 保存?zhèn)魅氲闹?    this.value=undefined;
    this.reason=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 添加微任務(wù)
        queueMicrotask(()=>{ //* 定時器是一個宏任務(wù),會放在下一次事件循環(huán)時使用
          if(this.status!==PROMISE_STATUS_PENDING) return;
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          // console.log("resolve被調(diào)用");
          // * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
         this.onFulfilledFns.forEach(Fn=>{
           Fn(this.value);
         })
        
      })
      }
     
     
    }
    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
      //  * 添加微任務(wù)
       queueMicrotask(()=>{ 
         if(this.status!==PROMISE_STATUS_PENDING) return;
         this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // console.log("reject被調(diào)用");
        // * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason)
        })
       })
      }
     
    }

    // * 在調(diào)用executor時判斷里面是否拋出異常
    try{
      executor(resolve,reject)
    }catch(err){
      reject(err)
    }
  }
  then(onFulfilled,onRejected){
   
    return new HYPromise((resolve,reject)=>{
       // 1.如果then方法調(diào)用的時候攘须,狀態(tài)已經(jīng)確定下來了漆撞,應(yīng)該直接執(zhí)行的
    if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
      execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
    }else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
      execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
    }else{
      // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
    this.onFulfilledFns.push(()=>{
      execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
    });
    this.onRejectedFns.push(()=>{
      execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
 });
    }
    })
  }
 
}

const promise=new HYPromise((resolve,reject)=>{
  // console.log("狀態(tài)pending");
  // resolve("1111");//resolved/fulfilled
  reject("2222")
  // throw new Error("創(chuàng)建就報的異常")
})

promise.then(res=>{
  console.log("res:",res);
  return "aaaa"

},err=>{
  console.log("err:",err);
  return "err bbb"
}).then(res=>{
  console.log("res2:",res);
},err=>{
  console.log("err2:",err);
})

// * 1. 實現(xiàn)了鏈?zhǔn)秸{(diào)用,鏈?zhǔn)秸{(diào)用的前提是返回一個新的HYPromise于宙,最重要的是 什么時候執(zhí)行resolve浮驳,什么時候執(zhí)行reject
// * 一般在正常情況下,無論原HYPromise的狀態(tài)是如何捞魁,后面都會執(zhí)行新的HYPromise的resolve至会,并將其值作為參數(shù)
// * 只有在代碼發(fā)生異常時(錯誤),才會讓新的HYPromise執(zhí)行reject

// * 2. 在HYPromise的executor執(zhí)行過程中如果發(fā)生異常谱俭,應(yīng)該要執(zhí)行reject


5.catch方法的設(shè)計

  • catch只會接受一個onRejected的回調(diào)函數(shù)
    • 在內(nèi)部調(diào)用then方法奉件,設(shè)置then方法的第一個參數(shù)為undefined,第二個參數(shù)的值為onRejected
  // * catch方法的設(shè)計
  catch(onRejected){
    this.then(undefined,onRejected);
  }

但是這樣還是不能捕獲原Promise對象的rejected狀態(tài),因為這個catch方法是針對新的Promise的rejected的狀態(tài)昆著,如果想要捕獲原Promise的rejected狀態(tài)县貌,需要拋出異常,新的Promise對象的catch方法才能捕獲到凑懂。

所以我們需要改寫then方法:

  • 在方法內(nèi)部前面判斷第二個參數(shù)是否有值煤痕,如果沒有值,就重新賦值為一個函數(shù),函數(shù)內(nèi)部拋出一個異常

這樣在新的Promise就能捕獲到原來的promise的rejected的狀態(tài)

 then(onFulfilled,onRejected){
   onRejected=onRejected||(err=>{throw err})
    //以下代碼省略...
  }

全部的代碼如下:

 const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    let result=exeFn(value);
    reject(result);
  }catch(err){
    reject(err);
  }
}
// * 自己手寫的Promise要符合的一定的規(guī)范摆碉,要符合PromiseA+
class HYPromise{
  // * 記錄狀態(tài)
  constructor(executor) {
    // * 保存Promise的狀態(tài)
    this.status=PROMISE_STATUS_PENDING;
    // * 保存?zhèn)魅氲闹?    this.value=undefined;
    this.reason=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 添加微任務(wù)
        queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)塘匣,會放在下一次事件循環(huán)時使用
          if(this.status!==PROMISE_STATUS_PENDING) return;
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          // console.log("resolve被調(diào)用");
          // * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
         this.onFulfilledFns.forEach(Fn=>{
           Fn(this.value);
         })
        
      })
      }
     
     
    }
    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
      //  * 添加微任務(wù)
       queueMicrotask(()=>{ 
         if(this.status!==PROMISE_STATUS_PENDING) return;
         this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // console.log("reject被調(diào)用");
        // * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason)
        })
       })
      }
     
    }

    // * 在調(diào)用executor時判斷里面是否拋出異常
    try{
      executor(resolve,reject)
    }catch(err){
      reject(err)
    }
  }
  then(onFulfilled,onRejected){
   onRejected=onRejected||(err=>{throw err})
    return new HYPromise((resolve,reject)=>{
       // 1.如果then方法調(diào)用的時候,狀態(tài)已經(jīng)確定下來了兆解,應(yīng)該直接執(zhí)行的
    if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
      execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
    }else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
      execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
    }else{
      // 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
    if(onFulfilled)this.onFulfilledFns.push(()=>{
      execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
    });
    if(onRejected)this.onRejectedFns.push(()=>{
      execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
 });
    }
    })
  }
 
  // * catch方法的設(shè)計
  catch(onRejected){
    this.then(undefined,onRejected);
  }
}

const promise=new HYPromise((resolve,reject)=>{
  console.log("狀態(tài)pending");
  // resolve("1111");//resolved/fulfilled
  reject("2222")
  // throw new Error("創(chuàng)建就報的異常")
})


promise.then(res=>{
  console.log("res:",res);
},err=>{
  console.log("err1:",err);
}).catch(err=>{ 
  console.log("err:",err);
})

6.finally方法的設(shè)計

const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)

function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    const result=exeFn(value);
    resolve(result);
  }catch(err){
    reject(err);
  }
}
class HYPromise{
  constructor(executor){
    this.status=PROMISE_STATUS_PENDING;
    this.value=undefined;
    this.reason=undefined;
    // this.onFulfilled=undefined;
    // this.onRejected=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 原生執(zhí)行then方法時是一個微任務(wù)
        queueMicrotask(()=>{
          if(this.status!==PROMISE_STATUS_PENDING) return; 
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          //  * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
          // this.onFulfilled(this.value);
          this.onFulfilledFns.forEach(Fn=>{
            Fn(this.value);
          })
        })
      }
     
    }

    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
       queueMicrotask(()=>{
         if(this.status!==PROMISE_STATUS_PENDING) return; 
        this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
        // this.onRejected(this.reason);
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason);
        })
       })

     }
    }
    try{
    executor(resolve,reject)
    }catch(err){
      reject(err);
    }
    
  }

  then(onFulfilled,onRejected){
     onRejected=onRejected|| (err=>{ throw err});
     onFulfilled=onFulfilled|| (value=>value)
    return new HYPromise((resolve,reject)=>{
      if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
      }else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
      }else{
        if(onFulfilled)this.onFulfilledFns.push(()=>{
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
        });
        if(onRejected)this.onRejectedFns.push(()=>{
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
        });
      }
    })
   
  }
  catch(onRejected){
    return this.then(undefined,onRejected);
  }

  finally(onFinally){
    return this.then(()=>{
      onFinally();
    },()=>{
      onFinally();
    })
  }
}

const promise=new HYPromise((resolve,reject)=>{
  console.log("狀態(tài)pending");
  // reject("111");
  resolve("222");
  // throw new Error("pending err")
  
})


promise.then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("catch err:",err);
}).finally(()=>{
  console.log("finally方法實現(xiàn)");
})
// * 實現(xiàn)finally方法


7.類方法 resolve和reject的設(shè)計

const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)

function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    const result=exeFn(value);
    resolve(result);
  }catch(err){
    reject(err);
  }
}
class HYPromise{
  constructor(executor){
    this.status=PROMISE_STATUS_PENDING;
    this.value=undefined;
    this.reason=undefined;
    // this.onFulfilled=undefined;
    // this.onRejected=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 原生執(zhí)行then方法時是一個微任務(wù)
        queueMicrotask(()=>{
          if(this.status!==PROMISE_STATUS_PENDING) return; 
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          //  * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
          // this.onFulfilled(this.value);
          this.onFulfilledFns.forEach(Fn=>{
            Fn(this.value);
          })
        })
      }
     
    }

    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
       queueMicrotask(()=>{
         if(this.status!==PROMISE_STATUS_PENDING) return; 
        this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
        // this.onRejected(this.reason);
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason);
        })
       })

     }
    }
    try{
    executor(resolve,reject)
    }catch(err){
      reject(err);
    }
    
  }

  then(onFulfilled,onRejected){
     onRejected=onRejected|| (err=>{ throw err});
     onFulfilled=onFulfilled|| (value=>value)
    return new HYPromise((resolve,reject)=>{
      if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
      }else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
      }else{
        if(onFulfilled)this.onFulfilledFns.push(()=>{
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
        });
        if(onRejected)this.onRejectedFns.push(()=>{
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
        });
      }
    })
   
  }
  catch(onRejected){
    return this.then(undefined,onRejected);
  }

  finally(onFinally){
    return this.then(()=>{
      onFinally();
    },()=>{
      onFinally();
    })
  }
  static resolve(value){
    return new HYPromise((resolve,reject)=>{
      resolve(value);
    })
  }
  static reject(reason){
    return new HYPromise((resolve,reject)=>{
      reject(reason);
    })
  }
}

HYPromise.resolve(123).then(res=>{
  console.log("res:",res);
})

HYPromise.reject("err message").catch(err=>{
  console.log("err:",err);
})


// * 實現(xiàn)了類方法 resolve和reject方法

 

8. 類方法 all 和allSettled

const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)

function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    const result=exeFn(value);
    resolve(result);
  }catch(err){
    reject(err);
  }
}
class HYPromise{
  constructor(executor){
    this.status=PROMISE_STATUS_PENDING;
    this.value=undefined;
    this.reason=undefined;
    // this.onFulfilled=undefined;
    // this.onRejected=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 原生執(zhí)行then方法時是一個微任務(wù)
        queueMicrotask(()=>{
          if(this.status!==PROMISE_STATUS_PENDING) return; 
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          //  * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
          // this.onFulfilled(this.value);
          this.onFulfilledFns.forEach(Fn=>{
            Fn(this.value);
          })
        })
      }
     
    }

    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
       queueMicrotask(()=>{
         if(this.status!==PROMISE_STATUS_PENDING) return; 
        this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
        // this.onRejected(this.reason);
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason);
        })
       })

     }
    }
    try{
    executor(resolve,reject)
    }catch(err){
      reject(err);
    }
    
  }

  then(onFulfilled,onRejected){
     onRejected=onRejected|| (err=>{ throw err});
     onFulfilled=onFulfilled|| (value=>value)
    return new HYPromise((resolve,reject)=>{
      if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
      }else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
      }else{
        if(onFulfilled)this.onFulfilledFns.push(()=>{
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
        });
        if(onRejected)this.onRejectedFns.push(()=>{
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
        });
      }
    })
   
  }
  catch(onRejected){
    return this.then(undefined,onRejected);
  }

  finally(onFinally){
    return this.then(()=>{
      onFinally();
    },()=>{
      onFinally();
    })
  }
  static resolve(value){
    return new HYPromise((resolve,reject)=>{
      resolve(value);
    })
  }
  static reject(reason){
    return new HYPromise((resolve,reject)=>{
      reject(reason);
    })
  }
  static all(promises){
    // * 問題關(guān)鍵:什么時候執(zhí)行resolve馆铁,什么時候執(zhí)行reject
   return new HYPromise((resolve,reject)=>{
     let values=[];
      promises.forEach(promise=>{
        promise.then(res=>{
           values.push(res);
           if(values.length==promises.length) resolve(values);
        }).catch(err=>{
           reject(err);
        })
      })
   })
  }
  static allSettled(promises){
    
    return new Promise((resolve)=>{
      let results=[];
      promises.forEach(promise=>{
        promise.then(res=>{
          results.push({status:PROMISE_STATUS_FULFILLED,value:res})
          if(results.length==promises.length)
          resolve(results);
        }).catch(err=>{
          results.push({status:PROMISE_STATUS_REJECTED,value:err})
          if(results.length==promises.length)
          resolve(results);
        })
      })
    })
  }
}

const p1=new Promise((resolve)=>{
  setTimeout(()=>{
    resolve(1111)
  },1000)
})

const p2=new Promise((resolve)=>{
  setTimeout(()=>{
    resolve(2222)
  },2000)
})

const p3=new Promise((resolve)=>{
  setTimeout(()=>{
    resolve(3333)
  },3000)
})
// *all方法會產(chǎn)生一個新的Promise對象,這個新的Promise對象是由前面的所有的Promise來決定其狀態(tài)
// * 如果全部為fulfilled锅睛,則會執(zhí)行then方法埠巨,如果有一個為rejected,則直接執(zhí)行catch方法
HYPromise.all([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})


// * allSettled:它會等所有的Promise都有結(jié)果现拒,然后再執(zhí)行resolve方法辣垒,它是沒有reject方法的執(zhí)行
// * 實現(xiàn)了all方法:

HYPromise.allSettled([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err);
})
 

9. 類方法 race和any

const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";

// 工具函數(shù)

function execFunctionWithCatchError(exeFn,value,resolve,reject){
  try{
    const result=exeFn(value);
    resolve(result);
  }catch(err){
    reject(err);
  }
}
class HYPromise{
  constructor(executor){
    this.status=PROMISE_STATUS_PENDING;
    this.value=undefined;
    this.reason=undefined;
    // this.onFulfilled=undefined;
    // this.onRejected=undefined;
    this.onFulfilledFns=[];
    this.onRejectedFns=[];
    const resolve=(value)=>{
      if(this.status==PROMISE_STATUS_PENDING){
        // * 原生執(zhí)行then方法時是一個微任務(wù)
        queueMicrotask(()=>{
          if(this.status!==PROMISE_STATUS_PENDING) return; 
          this.status=PROMISE_STATUS_FULFILLED;
          this.value=value;
          //  * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
          // this.onFulfilled(this.value);
          this.onFulfilledFns.forEach(Fn=>{
            Fn(this.value);
          })
        })
      }
     
    }

    const reject=(reason)=>{
      if(this.status==PROMISE_STATUS_PENDING){
       queueMicrotask(()=>{
         if(this.status!==PROMISE_STATUS_PENDING) return; 
        this.status=PROMISE_STATUS_REJECTED;
        this.reason=reason;
        // * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
        // this.onRejected(this.reason);
        this.onRejectedFns.forEach(Fn=>{
          Fn(this.reason);
        })
       })

     }
    }
    try{
    executor(resolve,reject)
    }catch(err){
      reject(err);
    }
    
  }

  then(onFulfilled,onRejected){
     onRejected=onRejected|| (err=>{ throw err});
     onFulfilled=onFulfilled|| (value=>value)
    return new HYPromise((resolve,reject)=>{
      if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
      }else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
      }else{
        if(onFulfilled)this.onFulfilledFns.push(()=>{
        execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
        });
        if(onRejected)this.onRejectedFns.push(()=>{
        execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
        });
      }
    })
   
  }
  catch(onRejected){
    return this.then(undefined,onRejected);
  }

  finally(onFinally){
    return this.then(()=>{
      onFinally();
    },()=>{
      onFinally();
    })
  }
  static resolve(value){
    return new HYPromise((resolve,reject)=>{
      resolve(value);
    })
  }
  static reject(reason){
    return new HYPromise((resolve,reject)=>{
      reject(reason);
    })
  }
  static all(promises){
    // * 問題關(guān)鍵:什么時候執(zhí)行resolve,什么時候執(zhí)行reject
   return new HYPromise((resolve,reject)=>{
     let values=[];
      promises.forEach(promise=>{
        promise.then(res=>{
           values.push(res);
           if(values.length==promises.length) resolve(values);
        }).catch(err=>{
           reject(err);
        })
      })
   })
  }
  static allSettled(promises){
    
    return new HYPromise((resolve)=>{
      let results=[];
      promises.forEach(promise=>{
        promise.then(res=>{
          results.push({status:PROMISE_STATUS_FULFILLED,value:res})
          if(results.length==promises.length)
          resolve(results);
        }).catch(err=>{
          results.push({status:PROMISE_STATUS_REJECTED,value:err})
          if(results.length==promises.length)
          resolve(results);
        })
      })
    })
  }

  static race(promises){
    return new HYPromise((resolve,reject)=>{
      promises.forEach(promise=>{
        // promise.then(res=>{
        //   resolve(res);
        // }).catch(err=>{
        //   reject(err);
        // })
        promise.then(resolve,reject)
      })
    })
  }
  static any(promises){
    // * resolve必須等待有一個成功的結(jié)果
    // * reject 所有的都失敗才執(zhí)行 reject
    return new HYPromise((resolve,reject)=>{
      let count=0;
      let reasons=[];
      promises.forEach(promise=>{
        promise.then(res=>{
          resolve(res)
        }).catch(err=>{
          reasons.push(err);
          if(reasons.length==promises.length){
            
            // reject(reasons)
            reject(new AggregateError(reasons," AggregateError: All promises were rejected"))
          }
        })
      })
    })
  }
}

// const p1=new Promise((resolve)=>{
//   setTimeout(()=>{
//     resolve(1111)
//   },1000)
// })

// const p2=new Promise((resolve)=>{
//   setTimeout(()=>{
//     resolve(2222)
//   },2000)
// })

// const p3=new Promise((resolve)=>{
//   setTimeout(()=>{
//     resolve(3333)
//   },3000)
// })

// HYPromise.race([p1,p2,p3]).then(res=>{
//   console.log("res:",res);
// }).catch(err=>{
//   console.log("err:",err);
// })

// * any方法
const p1=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(1111)
  },1000)
})

const p2=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(2222)
  },2000)
})

const p3=new Promise((resolve,reject)=>{
  setTimeout(()=>{
    reject(3333)
  },3000)
})

HYPromise.any([p1,p2,p3]).then(res=>{
  console.log("res:",res);
}).catch(err=>{
  console.log("err:",err.errors);
})

三印蔬、總結(jié)

Promise.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勋桶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子侥猬,更是在濱河造成了極大的恐慌例驹,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件退唠,死亡現(xiàn)場離奇詭異鹃锈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞧预,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門屎债,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人垢油,你說我怎么就攤上這事盆驹。” “怎么了滩愁?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵躯喇,是天一觀的道長。 經(jīng)常有香客問我惊楼,道長玖瘸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任檀咙,我火速辦了婚禮,結(jié)果婚禮上璃诀,老公的妹妹穿的比我還像新娘弧可。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布棕诵。 她就那樣靜靜地躺著裁良,像睡著了一般。 火紅的嫁衣襯著肌膚如雪校套。 梳的紋絲不亂的頭發(fā)上价脾,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音笛匙,去河邊找鬼侨把。 笑死,一個胖子當(dāng)著我的面吹牛妹孙,可吹牛的內(nèi)容都是我干的秋柄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蠢正,長吁一口氣:“原來是場噩夢啊……” “哼骇笔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嚣崭,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤笨触,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后雹舀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芦劣,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年葱跋,在試婚紗的時候發(fā)現(xiàn)自己被綠了持寄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡娱俺,死狀恐怖稍味,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荠卷,我是刑警寧澤模庐,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站油宜,受9級特大地震影響掂碱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慎冤,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一疼燥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚁堤,春花似錦醉者、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽立磁。三九已至,卻和暖如春剥槐,著一層夾襖步出監(jiān)牢的瞬間唱歧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工粒竖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留颅崩,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓温圆,卻偏偏與公主長得像挨摸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岁歉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容