如何手寫一個Promise

參考文章:《Promise,從入門到放棄》

為了方便比較與原裝的Promise區(qū)別养铸,手寫的Promise被命名為iPromise宽气。

實現(xiàn)本體

  1. 首先婴洼,Promise是一個類个少,接收一個函數(shù)executor作為構(gòu)造函數(shù),該函數(shù)接受兩個函數(shù)作為參數(shù):resolverejectPromise自帶庆亡,并不需要使用者手動部署),且立即(同步)執(zhí)行捞稿。

Promise對象的promiseResult屬性存儲執(zhí)行的結(jié)果又谋,promiseState屬性存儲狀態(tài)。
Promise有三種狀態(tài):pending(準(zhǔn)備中)娱局、fulfilled(滿足)和rejected(拒絕)彰亥。初始狀態(tài)是pending

class iPromise {
  constructor(executor) {
    // 存儲promise結(jié)果
    this.promiseResult = undefined;
    // 存儲promise的狀態(tài)
    this.promiseState = 'pending';
    
    // 立即執(zhí)行executor
    executor(resolve, reject);
  }

}
  1. resolve方法的作用是將執(zhí)行所得的結(jié)果賦值給promiseResult衰齐,并將promiseStatepending變?yōu)?code>fulfilled任斋。reject方法基本一樣,只是將promiseStatepending變?yōu)?code>rejected耻涛。

promise對象的狀態(tài)變化只能變化一次废酷;
executor執(zhí)行出現(xiàn)錯誤,也會觸發(fā)reject函數(shù)犬第。

class iPromise {
  constructor(executor) {
    // 存儲promise結(jié)果
    this.promiseResult = undefined;
    // 存儲promise的狀態(tài)
    this.promiseState = 'pending';
    
    // resolve方法锦积,將promiseState變?yōu)閒ulfilled,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
    }
    
    // reject方法歉嗓,將promiseState變?yōu)閞ejected丰介,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯誤,會調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }

}

實現(xiàn)then方法

  1. Promise.then()方法接收1~2個回調(diào)函數(shù)作為參數(shù)鉴分,返回一個新的Promise對象(下文中如果沒有特殊說明哮幢,Promise對象都指原Promise對象)以支持鏈?zhǔn)秸{(diào)用

返回的新Promise對象的參數(shù)必須使用箭頭函數(shù)()=>{}志珍,否則會造成this指向錯誤的問題橙垢。當(dāng)然,你也可以采取let self = this;存儲this然后傳入的形式伦糯,不過有點多此一舉柜某。

class iPromise {
  constructor(executor){
    // 構(gòu)造函數(shù)
  }
  
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      // ...
    })
  }
}
  1. 根據(jù)promiseResult的值不同敛纲,分為兩種情況:當(dāng)其為Promise對象時喂击,遞歸執(zhí)行它的then方法;當(dāng)其為其他類型淤翔,直接調(diào)用resolve方法翰绊。
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
    })
  }
}
  1. 根據(jù)promiseState的值來確定應(yīng)該執(zhí)行哪個回調(diào)函數(shù):
class iPromise {
  constructor(executor){
    // ...
  }
  
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù)监嗜,否則this會指向新的Promise對象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù)谐檀,this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState為fulfilled時調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState為rejected時調(diào)用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
    })
  }
}
  1. 因為異步任務(wù)的問題,并且支持多個回調(diào)裁奇,所以我們需要對回調(diào)函數(shù)采用數(shù)組進(jìn)行存儲桐猬,所以引入了新的變量:callbackList
class iPromise {
  constructor(executor) {
    // 存儲promise結(jié)果
    this.promiseResult = undefined;
    // 存儲promise的狀態(tài)
    this.promiseState = 'pending';
    // 存儲所有的回調(diào)函數(shù)
    this.callbackList = [];
    
    // resolve方法,將promiseState變?yōu)閒ulfilled框喳,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      this.callbackList.forEach(cb => cb.onResolved(value));
    }
    
    // reject方法课幕,將promiseState變?yōu)閞ejected,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      this.callbackList.forEach(cb => cb.onRejected(error));
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯誤五垮,會調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
  }
  
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    /*
    * 這里必須要寫箭頭函數(shù)乍惊,否則this會指向新的Promise對象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */ 
    return new iPromise((resolve, reject) => {
      
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù),this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
            let res = callback(this.promiseResult);
            // 若返回值是promise對象
            if (res instanceof Promise) {
              res.then(val => resolve(val), err => reject(err));
            } else {
              // 若不是
              resolve(res);
            }
        } catch (error) {
          reject(error);
        }
      }
      
      // promiseState為fulfilled時調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        handleCallback(onResolved);
      }
      
      // promiseState為rejected時調(diào)用onRejected
      if (this.promiseState === "rejected") {
        handleCallback(onRejected);
      }
      
      /*
       * 如果是pending狀態(tài)放仗,則異步任務(wù)润绎,在改變狀態(tài)的時候去調(diào)用回調(diào)函數(shù)
       * 所以要保存回調(diào)函數(shù)
       * 因為promise實例可以指定多個回調(diào),于是采用數(shù)組 
       */
      if (this.promiseState === "pending") {
        this.callbackList.push({
            onResolved: () => {
              handleCallback(onResolved)
            },
            onRejected: () => {
              handleCallback(onRejected)
            }
        })
      }
    })
  }
}

catch方法

catch方法主要需要做到的就是異常穿透:

當(dāng)使用promisethen進(jìn)行鏈?zhǔn)秸{(diào)用時诞挨,可以在最后指定失敗的回調(diào)莉撇。前面的任何錯誤都會在最后傳到失敗的回調(diào)中去處理,除非在中途被失敗回調(diào)函數(shù)(onRejected)處理了惶傻。

// promise對象的異常穿透
let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    throw 'err';
}).then((value)=>{
    console.log(22);
}).catch(err=>{
    console.log(err);
})

// 最終輸出:
// 11
// err

要實現(xiàn)catch棍郎,我們可以直接調(diào)用iPromise.then方法,但不傳入onResolve方法银室。

同時涂佃,我們需要給then中的onResolveonRejected賦初始值,順便避免了傳入undefined或其他非函數(shù)值而報錯:

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
  
    // 處理異常穿透蜈敢,并設(shè)置默認(rèn)值以避免程序出錯
    if(typeof onResolved !== 'function') {
      onResolve = (val) => val;
    }
    
    if(typeof onRejected !== 'function') {
      onRejected = (err) => {
        throw err;
      }
    }
    
    return new iPromise((resolve, reject) => {
      // ...調(diào)用回調(diào)函數(shù)
    })
  }
  
  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

Promise.resolve方法

Promise.resolve方法返回成功或者失敗的Promise對象辜荠。如果傳入的參數(shù)為非Promise類型的對象,則返回的結(jié)果為成功的Promise對象抓狭。如果傳入的參數(shù)為Promise對象伯病,則參數(shù)Promise返回的結(jié)果就是 Promise.resolve返回的結(jié)果。

let promiseA = Promise.resolve(1);

// 比如這時return 一個[[PromiseResult]]的值為err的Promise對象否过。
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
    reject('err');
})

實現(xiàn)它午笛,我們需要用到靜態(tài)方法

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // 靜態(tài)方法只能通過類本身來調(diào)用
  static resolve(value) {
    return new iPromise((resolve, reject) => {
      // 如果是iPromise對象
      if (value instanceof iPromise) {
        value.then(val => resolve(val), err => reject(err));
      } else {
        resolve(value);
      }
    })
  }
}

Promise.reject方法

Promise.reject方法返回一個失敗的Promise對象,promiseResult的值為Promise.reject參數(shù)苗桂。

有多失斠┗恰?大概像我一樣失敗誉察。

let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
    resolve('err');
})
// 無論傳入是啥与涡,就返回一個失敗的Promise對象惹谐,[[PromiseResult]]的值為 Promise.reject的參數(shù)

實現(xiàn):

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // 靜態(tài)方法只能通過類本身來調(diào)用
  static reject(error) {
    return new iPromise((resolve, reject) => {
      reject(error);
    })
  }
}

Promise.all方法

Promise.all方法接收的參數(shù)是由n個Promise對象的數(shù)組持偏。返回新的Promise對象驼卖,只有所有的Promise對象都成功才成功,返回的對象的promiseResult為包含所有Promise對象的數(shù)組鸿秆。只要有一個失敗了就直接失敗酌畜,返回的對象的promiseResult為失敗的Promise對象的執(zhí)行結(jié)果。

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  static all(promiseArrays) {
    return new iPromise((resolve, reject) => {
      // 用以存儲執(zhí)行的結(jié)果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多個異步任務(wù)的關(guān)系卿叽,需要判斷是否都執(zhí)行完畢
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有錯誤桥胞,則reject
          reject(err);
        });
      })
    })
  }
  
}

Promise.race方法

Promise.race方法接收的參數(shù)是由n個Promise對象的數(shù)組。返回新的Promise對象考婴,第一個完成的Promise的結(jié)果狀態(tài)就是最終結(jié)果的狀態(tài)贩虾。

你可能會問:那不鐵定第一個Promise對象是第一個完成的嗎?
這是因為我們的最重要的功能還沒做:異步沥阱。

class iPromise {
  constructor(executor) {
    // 構(gòu)造函數(shù)
  }
  
  // ...其他方法
  
  // race方法
  static race(promiseArrays) {
    return new iPromise((resolve, reject) => {
      promiseArrays.forEach(promiseObj => {
        promiseObj.then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      })
    })
  }
  
}

加點細(xì)節(jié)—由同步到異步

使用setTimeout將其變?yōu)楫惒饺蝿?wù)缎罢。

setTimeout只能將任務(wù)變更為宏觀異步任務(wù)。原裝的Promise是微觀異步任務(wù)考杉。

class iPromise {

  constructor(executor) {
    // 存儲promise結(jié)果
    this.promiseResult = undefined;
    // 存儲promise的狀態(tài)
    this.promiseState = 'pending';
    // 存儲所有的回調(diào)函數(shù)
    this.callbackList = [];
    
    // resolve方法策精,將promiseState變?yōu)閒ulfilled,并修改promiseResult
    const resolve = (value) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閒ulfilled
      this.promiseState = 'fulfilled';
      // 將value作為promiseResult
      this.promiseResult = value;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onResolved(value));
      })
    }
    
    // reject方法崇棠,將promiseState變?yōu)閞ejected咽袜,并修改promiseResult
    const reject = (error) => {
      // 僅在promiseState為pending的時候變化
      if (this.promiseState !== 'pending') return;
      // 將promiseState變?yōu)閞ejected
      this.promiseState = 'rejected';
      // 將error作為promiseResult
      this.promiseResult = error;
      // 異步執(zhí)行所有回調(diào)函數(shù)
      setTimeout(()=>{
        this.callbackList.forEach(cb => cb.onRejected(error));
      })
    }
    
    // 立即執(zhí)行executor
    // executor函數(shù)執(zhí)行出現(xiàn)錯誤,會調(diào)用reject方法
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // 接收兩個回調(diào)函數(shù)作為參數(shù)
  then(onResolved, onRejected) {
    //處理異常穿透并且為onResolved枕稀,onRejected設(shè)置默認(rèn)值询刹。因為這兩個參數(shù)可以都不傳
    if (typeof onRejected !== 'function') {
      onRejected = err => {
        throw err;
      }
    }
    if (typeof onResolved !== 'function') {
      onResolved = val => val;
    }
    /*
    * 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
    * 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
    */
    return new iPromise((resolve, reject) => {
      /*
      * 回調(diào)處理函數(shù)
      * 這里也請記得用箭頭函數(shù)抽莱,this要穿透幾層
      * 箭頭函數(shù)就用幾層
      */
      const handleCallback = (callback) => {
        try {
          let res = callback(this.promiseResult);
          // 若返回值是promise對象
          if (res instanceof iPromise) {
            res.then(val => resolve(val), err => reject(err));
          } else {
            // 若不是
            resolve(res);
          }
        } catch (error) {
          reject(error);
        }
      }
      // promiseState為fulfilled時調(diào)用onResolved
      if (this.promiseState === "fulfilled") {
        setTimeout(() => {
          handleCallback(onResolved);
        });
      }
      // promiseState為rejected時調(diào)用onRejected
      if (this.promiseState === "rejected") {
        setTimeout(() => {
          handleCallback(onRejected);
        });
      }
      /*
      * 如果是pending狀態(tài)范抓,則異步任務(wù),在改變狀態(tài)的時候去調(diào)用回調(diào)函數(shù)
      * 所以要保存回調(diào)函數(shù)
      * 因為promise實例可以指定多個回調(diào)食铐,于是采用數(shù)組 
      */
      if (this.promiseState === "pending") {
        this.callbackList.push({
          onResolved: () => {
            handleCallback(onResolved)
          },
          onRejected: () => {
            handleCallback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  static resolve(value) {
    return new iPromise((resolve, reject) => {
      if (value instanceof iPromise) {
        value.then(val => resolve(val), err => reject(err));
      } else {
        resolve(value)
      }
    })
  }

  static reject(error) {
    return new iPromise((resolve, reject) => {
      reject(error);
    })
  }

  static all(promiseArrays) {
    return new iPromise((resolve, reject) => {
      // 用以存儲執(zhí)行的結(jié)果
      let results = [];
      let length  = promiseArrays.length;
      promiseArrays.forEach((promiseObj, index, promiseArrays) => {
        promiseObj.then((val) => {
          results.push(val);
          // 由于是多個異步任務(wù)的關(guān)系匕垫,需要判斷是否都執(zhí)行完畢
          if (results.length === length) {
            resolve(results);
          }
        }, err => {
          // 如有錯誤,則reject
          reject(err);
        });
      })
    })
  }

  static race(promiseArrays) {
    return new iPromise((resolve, reject) => {
      promiseArrays.forEach(promiseObj => {
        promiseObj.then(val => {
          resolve(val);
        }, err => {
          reject(err);
        });
      });
    })
  }
}

總結(jié)

......沒啥好說的虐呻,從上到下敲一遍象泵,不說運用自如,怎么也得是一臉懵逼吧斟叼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偶惠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子朗涩,更是在濱河造成了極大的恐慌忽孽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兄一,居然都是意外死亡厘线,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門出革,熙熙樓的掌柜王于貴愁眉苦臉地迎上來造壮,“玉大人,你說我怎么就攤上這事骂束《担” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵展箱,是天一觀的道長旨枯。 經(jīng)常有香客問我,道長混驰,這世上最難降的妖魔是什么召廷? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮账胧,結(jié)果婚禮上竞慢,老公的妹妹穿的比我還像新娘。我一直安慰自己治泥,他們只是感情好筹煮,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著居夹,像睡著了一般败潦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上准脂,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天劫扒,我揣著相機與錄音,去河邊找鬼狸膏。 笑死沟饥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的湾戳。 我是一名探鬼主播贤旷,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砾脑!你這毒婦竟也來了幼驶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤韧衣,失蹤者是張志新(化名)和其女友劉穎盅藻,沒想到半個月后购桑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡氏淑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年其兴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夸政。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖榴徐,靈堂內(nèi)的尸體忽然破棺而出守问,到底是詐尸還是另有隱情,我是刑警寧澤坑资,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布耗帕,位于F島的核電站,受9級特大地震影響袱贮,放射性物質(zhì)發(fā)生泄漏仿便。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一攒巍、第九天 我趴在偏房一處隱蔽的房頂上張望嗽仪。 院中可真熱鬧,春花似錦柒莉、人聲如沸闻坚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窿凤。三九已至,卻和暖如春跨蟹,著一層夾襖步出監(jiān)牢的瞬間雳殊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工窗轩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夯秃,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓痢艺,卻偏偏與公主長得像寝并,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子腹备,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

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