我們從最簡(jiǎn)單的切入口開始恳守。
1.整體結(jié)構(gòu)的搭建
要實(shí)現(xiàn)的官方的功能
let p = new Promise((resolve,reject)=>{
resolve("ok");
});
p.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
觀察上面的結(jié)構(gòu):
-
Promise
構(gòu)造函數(shù)有一個(gè)回調(diào)函數(shù) 我們就給起名字為executor
- 實(shí)例對(duì)象上面有個(gè)
then
方法 -
then
方法有兩個(gè)回調(diào)函數(shù)onResolved
,onRejected
為了和Promise
做區(qū)分,這里起名字為PromiseA
目標(biāo)就明朗了,我們的實(shí)現(xiàn)版本如下:
function PromiseA(executor){
}
PromiseA.prototype.then = function (onResolved,onRejected){
}
測(cè)試用例:
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
});
p1.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
//沒有報(bào)錯(cuò),說明實(shí)現(xiàn)了我們最初的目的
2.resolve
和reject
的搭建
let p = new Promise((resolve,reject)=>{
resolve("ok");
});
p.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
2.1 還是觀察官方的例子,發(fā)現(xiàn)構(gòu)造函數(shù)中的回調(diào)函數(shù)中
構(gòu)造函數(shù)中的是同步調(diào)用的 怎么同步調(diào)用呢? 我們直接在構(gòu)造函數(shù)里面把回調(diào)函數(shù)exector
調(diào)用一下就可以
function PromiseA(executor){
//同步調(diào)用一下
executor();
}
2.2 同步調(diào)用以后發(fā)現(xiàn),回調(diào)函數(shù)里面還有兩個(gè)回調(diào)的參數(shù).resolve
和reject
.那么這兩個(gè)參數(shù)是哪里來的呢?他們是什么?
通過觀察發(fā)現(xiàn) resolve("ok")
.原來它們都是函數(shù),同時(shí)這個(gè)函數(shù)里面還有 調(diào)用的時(shí)候傳入的參數(shù).好,安排.
function PromiseA(executor){
function resolve(data){
}
function reject(data){
}
//同步調(diào)用一下
executor(resolve,reject);
}
3.resolve
和reject
的實(shí)現(xiàn)
在之前的研究中我們知道resolve("OK")
被調(diào)用的時(shí)候,它會(huì)有以下兩個(gè)作用:
- 修改實(shí)例對(duì)象Promise的狀態(tài)為
fulfilled
. - 它傳入的參數(shù)
ok
就是結(jié)果值
所以我們需要實(shí)現(xiàn)這兩步:
- 修改狀態(tài)---那么就需要一個(gè)初始狀態(tài)
- 改變結(jié)果值---那么就需要一個(gè)存儲(chǔ)結(jié)果值的參數(shù)
我們看一下官網(wǎng)Promise
的內(nèi)部狀態(tài)
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "OK"
可以看到它內(nèi)部有兩個(gè)內(nèi)部屬性:PromiseState
和PromiseResult
.那我們就知道了怎么做了.
function PromiseA(executor){
//添加屬性
this.PromiseState = "pending";
this.PromiseResult = null;
//resolve函數(shù)
function resolve(data){
//1.修改對(duì)象的狀態(tài)(PromiseState)
this.PromiseState = "fulfilled";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
this.PromiseResult = data;
}
function reject(data){
}
//同步調(diào)用一下
executor(resolve,reject);
}
這里有一個(gè)問題,我們調(diào)用上面的實(shí)例會(huì)發(fā)現(xiàn):
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
});
console.log(p1)
// PromiseA { PromiseState: 'pending', PromiseResult: null }
我們的修改這里并沒有生效,這是因?yàn)楹瘮?shù)在單獨(dú)的環(huán)境中調(diào)用,它是指向window
的.所以我們這里可以借助詞法作用域的特性來解決這個(gè)問題. 通過設(shè)定一個(gè)selt
來保存this
的值
function PromiseA(executor){
//添加屬性
this.PromiseState = "pending";
this.PromiseResult = null;
//預(yù)先保存實(shí)例對(duì)象的this值
const self = this;
//resolve函數(shù)
function resolve(data){
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "fulfilled";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "rejected";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
}
//同步調(diào)用一下
executor(resolve,reject);
}
reject
同理 ,就實(shí)現(xiàn)了我們上面的這一版
4. throw 拋出異常改變狀態(tài)
這一節(jié)我們實(shí)現(xiàn)的狀態(tài)是 throw
拋出異常會(huì)改變狀態(tài).
let p1 = new Promise((resolve,reject)=>{
throw "error"
});
console.log(p1)
Promise {<rejected>: 'error'}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "error"
可以看到官放的Promise
在其內(nèi)部報(bào)錯(cuò)的時(shí)候,它會(huì)把內(nèi)部的throw
處理掉,同時(shí)把狀態(tài)修改,
把錯(cuò)誤的結(jié)果放到PromiseResult
中.
function PromiseA(executor){
//添加屬性
this.PromiseState = "pending";
this.PromiseResult = null;
//預(yù)先保存實(shí)例對(duì)象的this值
const self = this;
//resolve函數(shù)
function resolve(data){
//...
}
function reject(data){
//...
}
//-------------- 修改的代碼-----------------
try{
//同步調(diào)用一下
executor(resolve,reject);
}catch (e){
//通過調(diào)用reject函數(shù),它的內(nèi)部可以修改狀態(tài)的和賦值
//所以我們這里可以把錯(cuò)誤直接傳進(jìn)去就可以了.
reject(e);
}
//-------------- 修改的代碼-----------------
}
既然要處理錯(cuò)誤,我們就添加一個(gè)try catch
然后通過reject
來處理內(nèi)部的狀態(tài)和數(shù)據(jù).
看一下測(cè)試用例的輸出:
let p1 = new PromiseA((resolve,reject)=>{
throw "error"
});
console.log(p1)
//PromiseA { PromiseState: 'rejected', PromiseResult: 'error' }
5.內(nèi)部的狀態(tài)只能修改一次
內(nèi)部狀態(tài)只能修改一次,就是說 我們先調(diào)用了resolve
,然后調(diào)用reject
,并不能把狀態(tài)fulfilled
修改為了rejected
.
那么怎么實(shí)現(xiàn)呢? 只需要添加個(gè)判斷就好
function PromiseA(executor){
//添加屬性
this.PromiseState = "pending";
this.PromiseResult = null;
//預(yù)先保存實(shí)例對(duì)象的this值
const self = this;
//resolve函數(shù)
function resolve(data){
//-------------- 修改的代碼-----------------
//判斷狀態(tài)
if(self.PromiseState !== "pending"){
return ;
}
//-------------- 修改的代碼-----------------
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "fulfilled";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//-------------- 修改的代碼-----------------
//判斷狀態(tài)
if(self.PromiseState !== "pending"){
return ;
}
//-------------- 修改的代碼-----------------
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "rejected";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
}
//處理 throw 拋出的錯(cuò)誤
try{
//同步調(diào)用一下
executor(resolve,reject);
}catch (e){
//通過調(diào)用reject函數(shù),它的內(nèi)部可以修改狀態(tài)的和賦值
//所以我們這里可以把錯(cuò)誤直接傳進(jìn)去就可以了.
reject(e);
}
}
測(cè)試用例: 可以看到我們的狀態(tài)并沒有被再次改變?yōu)?code>rejected
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
reject("error");
});
console.log(p1)
//PromiseA { PromiseState: 'fulfilled', PromiseResult: 'ok' }
6.then方法的實(shí)現(xiàn)
官方的then
方法接收兩個(gè)函數(shù)的回調(diào)參數(shù),分別對(duì)應(yīng)于成功的情況和失敗的情況.
當(dāng)new Promise
的構(gòu)造函數(shù)中調(diào)用resolve
,那么對(duì)應(yīng)的then
方法中就會(huì)執(zhí)行第一個(gè)回調(diào)函數(shù).
當(dāng)new Promise
的構(gòu)造函數(shù)中調(diào)用reject
,那么對(duì)應(yīng)的then
方法中就會(huì)執(zhí)行第二個(gè)回調(diào)函數(shù).
let p = new Promise((resolve,reject)=>{
throw "error"
});
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
所以我們的then
方法的兩個(gè)形參 對(duì)應(yīng)的 Promise
的兩個(gè)實(shí)參. onResolved
和onRejected
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
}
既然構(gòu)造函數(shù) 執(zhí)行resolve
,就會(huì)調(diào)用onResolved
,所以需要在then
方法中進(jìn)行調(diào)用.
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//調(diào)用 成功 回調(diào)函數(shù)
onResolved();
//調(diào)用失敗的回調(diào)函數(shù)
onRejected();
}
但是這樣調(diào)用的話,且不是兩個(gè)都調(diào)用了,我們需要區(qū)分情況來對(duì)待.成功的時(shí)候調(diào)用onResolved
,
失敗的時(shí)候調(diào)用onRejected
.所以應(yīng)該怎么區(qū)分情況呢?
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//調(diào)用 成功 回調(diào)函數(shù)
if(this.PromiseState === "fulfilled"){
onResolved();
}
//調(diào)用失敗的回調(diào)函數(shù)
if(this.PromiseState === "rejected"){
onRejected();
}
}
既然then
方法是構(gòu)造函數(shù)執(zhí)行完畢返回實(shí)例之后,通過實(shí)例調(diào)用的.這里就有了隱式綁定this
.
這個(gè)時(shí)候this
綁定的是實(shí)例對(duì)象p
.所以我們這個(gè)時(shí)候可以通過this
,拿到實(shí)例對(duì)象中的狀態(tài)和數(shù)據(jù).
PromiseState
和PromiseResult
.
then
方法的兩個(gè)函數(shù)回調(diào)中,會(huì)返回實(shí)例對(duì)象執(zhí)行結(jié)果的數(shù)據(jù),所以我們?cè)谡{(diào)用onResolved
和onRejected
的時(shí)候,給它們傳入實(shí)例的執(zhí)行結(jié)果 PromiseResult
.
所以最后就是這樣:
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//調(diào)用 成功 回調(diào)函數(shù)
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//調(diào)用失敗的回調(diào)函數(shù)
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
}
7.異步任務(wù) then 方法實(shí)現(xiàn)
異步任務(wù)的實(shí)現(xiàn)就是:
let p = new PromiseA((resolve,reject)=>{
//異步調(diào)用
setTimeout(()=>{
resolve("OK");
},100)
});
console.log(p);
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
//PromiseA { PromiseState: 'pending', PromiseResult: null }
我們之前在構(gòu)造函數(shù)中都是同步調(diào)用resolve
,在執(zhí)行的下面的then
的時(shí)候,實(shí)際上狀態(tài)已經(jīng)修改為
fulfilled
或者rejected
.
而異步調(diào)用的話,可以看上面的打印輸出.實(shí)例的狀態(tài)是pending
.它的狀態(tài)是在未來的某個(gè)時(shí)間進(jìn)行的改變.
而我們的then
方法中,卻并沒有對(duì)pending
狀態(tài)的處理,所以我們需要給它添加一下.
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//調(diào)用 成功 回調(diào)函數(shù)
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//調(diào)用失敗的回調(diào)函數(shù)
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
//添加pending狀態(tài)的處理
if(this.PromiseState === "pending"){
//...
}
}
那這里應(yīng)該怎么處理呢?
由于是異步調(diào)用的原因,當(dāng)同步執(zhí)行到then
方法的時(shí)候,它的內(nèi)部狀態(tài)還是pending
,所以給同步代碼使用的
//調(diào)用 成功 回調(diào)函數(shù)
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//調(diào)用失敗的回調(diào)函數(shù)
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
就用不上了,而內(nèi)部狀態(tài)的改變是在未來的某個(gè)地方進(jìn)行的? 是在哪里呢?
function PromiseA(executor){
//添加屬性
//....
//resolve函數(shù)
function resolve(data){
//判斷狀態(tài)
if(self.PromiseState !== "pending"){
return ;
}
//--------------- 在未來某個(gè)地方改變狀態(tài)的就是這里----------
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "fulfilled";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//判斷狀態(tài)
//....
}
//處理 throw 拋出的錯(cuò)誤
//...
}
所以我們需要在狀態(tài)改變的地方去調(diào)用then
方法的回調(diào). ,也就是需要一個(gè)變量把then
方法的兩個(gè)回調(diào)函數(shù)
保存起來,然后在構(gòu)造函數(shù)resolve
的改變狀態(tài)的地方進(jìn)行調(diào)用.所以我們定義一個(gè)callback
變量來存儲(chǔ)回調(diào)參數(shù).
function PromiseA(executor){
//添加屬性
this.PromiseState = "pending";
this.PromiseResult = null;
//-------------- 修改的代碼-----------------
this.callback = {};
//-------------- 修改的代碼-----------------
//預(yù)先保存實(shí)例對(duì)象的this值
const self = this;
//resolve函數(shù)
function resolve(data){
//判斷狀態(tài)
if(self.PromiseState !== "pending"){
return ;
}
//1.修改對(duì)象的狀態(tài)(PromiseState)
self.PromiseState = "fulfilled";
//2.設(shè)置對(duì)象的結(jié)果值(PromiseResult)
self.PromiseResult = data;
//-------------- 修改的代碼-----------------
//調(diào)用成功的回調(diào)函數(shù)
if(self.callback.onResolved){
//參數(shù)是成功的結(jié)果
self.callback.onResolved(data);
}
//-------------- 修改的代碼-----------------
}
function reject(data){
//判斷狀態(tài)
// ...
}
//處理 throw 拋出的錯(cuò)誤
//....
}
我們?cè)?code>then方法的中判斷pending
來保存callback
;
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//調(diào)用 成功 回調(diào)函數(shù)
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//調(diào)用失敗的回調(diào)函數(shù)
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
//-------------- 修改的代碼-----------------
//添加pending狀態(tài)的處理
if(this.PromiseState === "pending"){
//保存回調(diào)函數(shù)
this.callback = {
onResolved,
onRejected
};
}
//-------------- 修改的代碼-----------------
}
測(cè)試用例:
let p = new PromiseA((resolve,reject)=>{
//異步調(diào)用
setTimeout(()=>{
resolve("OK");
},100)
});
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
//輸出 OK