手撕源碼系列 —— 函子 + 觀察者模式 + 狀態(tài) = Promise

前言

前段時(shí)間太忙蝇棉,隔了快一個(gè)月沒寫博客讨阻,但是 Promise 其實(shí)很早之前就已經(jīng)總結(jié)了一波如何實(shí)現(xiàn),但是那個(gè)時(shí)候純粹是為了實(shí)現(xiàn)而實(shí)現(xiàn)篡殷,沒有去細(xì)品其中的一些巧妙設(shè)計(jì)钝吮,直到最近在進(jìn)行函數(shù)式編程相關(guān)的知識(shí)學(xué)習(xí)時(shí),無意中在查閱資料的時(shí)候發(fā)現(xiàn),PromiseFunctor 居然有著千絲萬縷的關(guān)系奇瘦,這讓我決定要重新審視一下自己對(duì) Promise 的認(rèn)知棘催,于是便有了這篇“老酒新裝”的博客。

前置知識(shí)

想要完全閱讀并理解這篇博客耳标,我粗略地估算了一下醇坝,大概需要以下的一些前置知識(shí),不了解的同學(xué)可以自行先去學(xué)習(xí)一下:

  • 遞歸的思想
  • ES6Typescript 的基礎(chǔ)認(rèn)知
  • 函數(shù)式編程中的函子(Functor)和“函數(shù)是一等公民”思想
  • 設(shè)計(jì)模式中的觀察者模式

分步實(shí)現(xiàn) Promise

我們可以將實(shí)現(xiàn)一個(gè) Promise 比作蓋一棟樓次坡,通過拆分每一步并解決呼猪、理解和記憶,達(dá)到很快就能理解它的實(shí)現(xiàn)的目的砸琅。

一宋距、打下 Promise 的地基

由于我們常常使用 Promise 配合 async/await 來實(shí)現(xiàn)異步編程,所以先回想一下如何最最基本地使用 Promise

new Promise(resolve => {...})
    .then(onFulfilled)
    .then(onFulfilled)

根據(jù)上面我們回憶中的最最基本的 Promise 我們可以寫出以下實(shí)現(xiàn):

class Promise {
    constructor(executor) {
        const resolve = () => {}
        
        executor(resolve)
    }
    
    then = onFulfilled => new Promise(...)
}

好的症脂,那我們的第一步到這里就結(jié)束了乡革,是不是十分簡單,完全不需要什么成本就能理解并記憶下來摊腋。

二沸版、加入原材料 函子 和 觀察者模式

這一步,我們開始往里加?xùn)|西了兴蒸。對(duì)于加的東西视粮,我們也必須了解是什么,所以先來看下兩個(gè)原材料的基本概念橙凳。

函子

由于在 前置知識(shí) 里提到蕾殴,相信大家已經(jīng)對(duì)它有所了解,一個(gè)基本的函子我們可以寫成以下實(shí)現(xiàn):

class Functor {
    static of = value => new Functor(this.value)
    
    constructor(value) {
        this.value = value
    }
    
    map = fn => Functor.of(fn(this.value))
}

為了方便映射到 Promise 的實(shí)現(xiàn)中岛啸,改為以下寫法:

class Functor {
    constructor(value) {
        this.value = value
    }
    
    map = fn => new Functor(fn(this.value))
}

然后結(jié)合到函子的一些特性:

const plus = x + y => x + y
const plusOne = x => plus(x, 1)

new Functor(100)
    .map(plusOne)
    .map(plusOne) // { value: 102 }

這個(gè)是時(shí)候再發(fā)揮我們從小被培養(yǎng)的找規(guī)律能力钓觉,我們發(fā)現(xiàn):

  1. 兩者的結(jié)構(gòu)類似,擁有一個(gè)方法對(duì)內(nèi)部的數(shù)據(jù)進(jìn)行操作
  2. 兩者均可進(jìn)行鏈?zhǔn)秸{(diào)用

通過以上兩點(diǎn)可以得到一個(gè)結(jié)論坚踩,之所以引入函子荡灾,可以解決鏈?zhǔn)秸{(diào)用的問題,但是光有一個(gè)函子不夠呀瞬铸,函子只能實(shí)現(xiàn)同步的鏈?zhǔn)秸{(diào)用批幌,這時(shí)候另外一個(gè)原材料觀察者模式就出場(chǎng)了。

觀察者模式

先看一個(gè)簡單的觀察者模式實(shí)現(xiàn):

class Observer {
  constructor() {
    this.callbacks = []

    this.notify = value => {
      this.callbacks.forEach(observe => observe(value))
    }
  }

  subscribe = observer => {
    this.callbacks.push(observer)
  }
}

這時(shí)候聰明的人一下就發(fā)現(xiàn)了嗓节,這個(gè) notifysubscribe 不就是 resolvethen 嘛荧缘!

俺のターン!ドロー拦宣!魔法発動(dòng)截粗!

c21f67221889af25edb36826285ce8aa_hd.jpg

ターンエンド信姓!

class Promise {
    constructor(executor) {
        this.value = undefined
        this.callbacks = []
        
        // 相當(dāng)于 notify
        const resolve = value => {
            this.value = value
            this.callbacks.forEach(callback => callback())
        }
        
        executor(resolve)
    }
    
    // 相當(dāng)于 subscribe 或 map
    then = onFulfilled => new Promise(resolve => {
        this.callbacks.push(() => resolve(onFulfilled(this.value)))
    })
}

融合后的初級(jí) Promise 已經(jīng)具有異步鏈?zhǔn)秸{(diào)用的能力了比如:

const promise = new Promise(resolve => {
    setTimeout(() => {
        resolve(100)
    }, 500)
})
    .map(plusOne)
    .map(plusOne)
    // { value: 102 }

但是當(dāng)我們進(jìn)行一些騷操作時(shí),依然會(huì)出問題:

const promise = new Promise(resolve => {
    setTimeout(() => {
        resolve(100)
        resolve(1000)
    }, 500)
})
    .map(plusOne)
    .map(plusOne)
    // { value: 1002 }

為了解決這個(gè)問題绸罗,我們還需要一個(gè)原材料狀態(tài)意推。

篇幅有限,這一部分更細(xì)致的轉(zhuǎn)換過程从诲,我的 repo 都有記錄左痢。

三、加入原材料 狀態(tài)

眾所周知系洛,<span style="text-decoration-line: line-through">“青眼究極龍需要三條青眼白龍”</span>俊性,為了解決上一部分留下的問題,這一部分描扯,需要給 Promise 加入狀態(tài)這個(gè)原材料定页。

class Promise {
  static PENDING = 'PENDING'
  static FULFILLED = 'FULFILLED'

  constructor(executor) {
    this.value = undefined
    this.callbacks = []
    this.status = Promise.PENDING

    // 一系列操作(狀態(tài)的改變,成功回調(diào)的執(zhí)行)
    const resolve = value => {
      // 只有處于 pending 狀態(tài)的 promise 能調(diào)用 resolve
      if (this.status === Promise.PENDING) {
        // resolve 調(diào)用后绽诚,status 轉(zhuǎn)為 fulfilled
        this.status = Promise.FULFILLED
        // 儲(chǔ)存 fulfilled 的終值
        this.value = value
        // 一旦 resolve 執(zhí)行典徊,調(diào)用儲(chǔ)存在回調(diào)數(shù)組里的回調(diào)
        this.callbacks.forEach(callback => callback())
      }
    }

    executor(resolve)
  }

  then = onFulfilled =>
    new Promise(resolve => {
      // 當(dāng) status 為執(zhí)行態(tài)(Fulfilled)時(shí)
      if (this.status === Promise.FULFILLED) {
        resolve(onFulfilled(this.value))
      }
      // 當(dāng) status 為 Pending 時(shí)
      if (this.status === Promise.PENDING) {
        // 將 onFulfilled 存入回調(diào)數(shù)組
        this.callbacks.push(() => resolve(onFulfilled(this.value)))
      }
    })
}

至此,通過三大原材料構(gòu)建出的 Promise 就完成了恩够,當(dāng)然卒落,還有很多功能沒有實(shí)現(xiàn),<span style="text-decoration-line: line-through">魯迅曾經(jīng)說過:</span>“要站在巨人的肩膀上看問題蜂桶±鼙希”,下一步扑媚,就需要 Promise/A+ 規(guī)范來來幫助我們實(shí)現(xiàn)一個(gè)具有完整功能的 Promise腰湾。

四、打開設(shè)計(jì)圖紙 Promise/A+ 規(guī)范

劍來疆股! Promise/A+ 規(guī)范费坊,接下來的操作,需要跟著它一步一步進(jìn)行旬痹。

1附井、加入拒絕態(tài)以及處理(reject and onRejected)

其實(shí)這一步不用規(guī)范我們也知道,Promise 擁有的終態(tài)fulfilledrejected 兩種唱凯,所以要把剩下的 rejected 以及一些相關(guān)操作給補(bǔ)上羡忘。

class Promise {
  ......
  static REJECTED = 'REJECTED'
  
  constructor(executor) {
    this.value = undefined
    this.reason = undefined
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    this.status = PromiseFunctorWithTwoStatus.PENDING

    // 成功后的一系列操作(狀態(tài)的改變,成功回調(diào)的執(zhí)行)
    const resolve = value => {
        ......
    }
    
    // 失敗后的一系列操作(狀態(tài)的改變磕昼,失敗回調(diào)的執(zhí)行)
    const reject = reason => {
      // 只有處于 pending 狀態(tài)的 promise 能調(diào)用 resolve
      if (this.status === Promise.PENDING) {
        // reject 調(diào)用后,status 轉(zhuǎn)為 rejected
        this.status = Promise.REJECTED
        // 儲(chǔ)存 rejected 的拒因
        this.reason = reason
        // 一旦 reject 執(zhí)行节猿,調(diào)用儲(chǔ)存在失敗回調(diào)數(shù)組里的回調(diào)
        this.onRejectedCallbacks.forEach(onRejected => onRejected())
      }
    }

    executor(resolve, reject)
  }

  then = (onFulfilled, onRejected) =>
    new Promise(resolve => {
      // 當(dāng) status 為執(zhí)行態(tài)(Fulfilled)時(shí)
      ......
      
      // 當(dāng) status 為拒絕態(tài)(Rejected)時(shí)
      if (this.status === PromiseFunctorWithTwoStatus.REJECTED) {
        reject(onRejected(this.reason))
      }
      
      // 當(dāng) status 為 Pending 時(shí)
      if (this.status === Promise.PENDING) {
        // 將 onFulfilled 存入回調(diào)數(shù)組
        this.onFulfilledCallbacks.push(() => resolve(onFulfilled(this.value)))
        // 將 onRejected 存入失敗回調(diào)數(shù)組
        this.onRejectedCallbacks.push(() => reject(onRejected(this.reason)))
      }
    })
}

2票从、加入核心 resolvePromise 方法實(shí)現(xiàn)解決過程

Promise 解決過程是一個(gè)抽象的操作漫雕,其需輸入一個(gè) promise 和一個(gè)值,我們表示為 [[Resolve]](promise, x)峰鄙,如果 x 有 then 方法且看上去像一個(gè) Promise 浸间,解決程序即嘗試使 promise 接受 x 的狀態(tài)吟榴;否則其用 x 的值來執(zhí)行 promise 魁蒜。

這種 thenable 的特性使得 Promise 的實(shí)現(xiàn)更具有通用性:只要其暴露出一個(gè)遵循 Promise/A+ 協(xié)議的 then 方法即可;這同時(shí)也使遵循 Promise/A+ 規(guī)范的實(shí)現(xiàn)可以與那些不太規(guī)范但可用的實(shí)現(xiàn)能良好共存锌妻。

根據(jù)規(guī)范的描述从祝,我們依照他給的實(shí)現(xiàn)步驟襟己,寫出代碼實(shí)現(xiàn):

class Promise {
    ......
    static resolvePromise = (anotherPromise, x, resolve, reject) => {
      // 如果 onFulfilled 或者 onRejected 返回一個(gè)值 x ,則運(yùn)行下面的 Promise 解決過程:[[Resolve]](promise2, x)
      // 運(yùn)行 [[Resolve]](promise, x) 需遵循以下步驟:
        
      // 如果 promise 和 x 指向同一對(duì)象牍陌,以 TypeError 為拒因拒絕執(zhí)行 promise 以防止循環(huán)引用
      if (anotherPromise === x) {
        return reject(new TypeError('Chaining cycle detected for promise'))
      }
      
      // 如果 x 為 Promise 擎浴,則使 promise 接受 x 的狀態(tài)
      if (x instanceof Promise) {
          x.then(
          // 如果 x 處于執(zhí)行態(tài),用相同的值執(zhí)行 promise
          value => {
            return Promise.resolvePromise(anotherPromise, value, resolve, reject)
          },
          // 如果 x 處于拒絕態(tài)毒涧,用相同的拒因拒絕 promise
          reason => {
            return reject(reason)
          }
        )
        // 如果 x 為對(duì)象或者函數(shù)
      } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
          let called = false
          try {
              // 把 x.then 賦值給 then(這步我們先是存儲(chǔ)了一個(gè)指向 x.then 的引用贮预,然后測(cè)試并調(diào)用該引用,以避免多次訪問 x.then 屬性契讲。這種預(yù)防措施確保了該屬性的一致性仿吞,因?yàn)槠渲悼赡茉跈z索調(diào)用時(shí)被改變。)
              const then = x.then
              // 如果 then 是函數(shù)捡偏,將 x 作為函數(shù)的作用域 this 調(diào)用之唤冈。傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù),
              if (typeof then === 'function') {
                then.call(
                    x,
                    // 第一個(gè)參數(shù)叫做 resolvePromise 银伟,
                    value => {
                        // 如果 resolvePromise 和 rejectPromise 均被調(diào)用你虹,或者被同一參數(shù)調(diào)用了多次绘搞,則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
                        if (called) {
                            return 
                        }
                        called = true
                        // 如果 resolvePromise 以值 y 為參數(shù)被調(diào)用,則運(yùn)行 [[Resolve]](promise, y)
                        return Promise.resolvePromise(
                          anotherPromise,
                          value,
                          resolve,
                          reject
                        )
                    },
                    // 第二個(gè)參數(shù)叫做 rejectPromise
                    reason => {
                        // 如果 resolvePromise 和 rejectPromise 均被調(diào)用傅物,或者被同一參數(shù)調(diào)用了多次夯辖,則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
                        if (called) {
                          return
                        }
                        called = true
                        // 如果 rejectPromise 以拒因 r 為參數(shù)被調(diào)用,則以拒因 r 拒絕 promise
                        return reject(reason)
                    }
                )
              } else {
                  //如果 then 不是函數(shù)董饰,以 x 為參數(shù)執(zhí)行 promise
                  return resolve(x)
              }
          } catch (error) {
              // 如果調(diào)用 then 方法拋出了異常 e, 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用蒿褂,則忽略之
              if (called) {
                  return
              }
              called = true
              // 如果取 x.then 的值時(shí)拋出錯(cuò)誤 e ,則以 e 為拒因拒絕 promise
              return reject(error)
          }
      } else {
          // 如果 x 不為對(duì)象或者函數(shù)卒暂,以 x 為參數(shù)執(zhí)行 promise
          return resolve(x)
      }
    }
    ......
}

同時(shí)啄栓,我們要對(duì)之前的 Promise 里的 resolve 方法進(jìn)行改造:

class Promise {
    ......
    constructor(executor) {
        ......
        // 成功后的一系列操作(狀態(tài)的改變,成功回調(diào)的執(zhí)行)
        const resolve = x => {
          const __resolve = value => {
            // 只有處于 pending 狀態(tài)的 promise 能調(diào)用 resolve
            if (this.status === Promise.PENDING) {
              // resolve 調(diào)用后介却,status 轉(zhuǎn)為 fulfilled
              this.status = Promise.FULFILLED
              // 儲(chǔ)存 fulfilled 的終值
              this.value = value
              // 一旦 resolve 執(zhí)行谴供,調(diào)用儲(chǔ)存在成功回調(diào)數(shù)組里的回調(diào)
              this.onFulfilledCallbacks.forEach(onFulfilled => onFulfilled())
            }
          }
          return Promise.resolvePromise.call(this, this, x, __resolve, reject)
        }
        ......
    }
    ......
}
class Promise {
    ......
    then = (onFulfilled, onRejected) => {
        // then 方法必須返回一個(gè) promise 對(duì)象
        const anotherPromise = new Promise((resolve, reject) => {
          // 封裝處理鏈?zhǔn)秸{(diào)用的方法
          const handle = (fn, argv) => {
            // 確保 onFulfilled 和 onRejected 方法異步執(zhí)行
            setTimeout(() => {
              try {
                const x = fn(argv)
                return Promise.resolvePromise(anotherPromise, x, resolve, reject)
              } catch (error) {
                return reject(error)
              }
            })
          }
          // 當(dāng) status 為執(zhí)行態(tài)(Fulfilled)時(shí)
          if (this.status === Promise.FULFILLED) {
            // 則執(zhí)行 onFulfilled,value 作為第一個(gè)參數(shù)
            handle(onFulfilled, this.value)
          }
          // 當(dāng) status 為拒絕態(tài)(Rejected)時(shí)
          if (this.status === Promise.REJECTED) {
            // 則執(zhí)行 onRejected齿坷,reason 作為第一個(gè)參數(shù)
            handle(onRejected, this.reason)
          }
    
          // 當(dāng) status 為 Pending 時(shí)
          if (this.status === Promise.PENDING) {
            // 將 onFulfilled 存入成功回調(diào)數(shù)組
            this.onFulfilledCallbacks.push(() => {
              handle(onFulfilled, this.value)
            })
            // 將 onRejected 存入失敗回調(diào)數(shù)組
            this.onRejectedCallbacks.push(() => {
              handle(onRejected, this.reason)
            })
          }
        })
    
        return anotherPromise
    }
    ......
}

3桂肌、加入 其它方法 完善周邊

Promise 的主體已經(jīng)寫好了,接下來要實(shí)現(xiàn)其他的一些輔助方法來完善它永淌。

  • catch
catch = onRejected => {
    return this.then(null, onRejected)
}
  • finally
finally = fn => {
    return this.then(
        value => {
            setTimeout(fn)
            return value
        },
        reason => {
            setTimeout(fn)
            throw reason
        }
    )
}
  • resolve
static resolve = value => new Promise((resolve, reject) => resolve(value))
  • reject
static reject = reason => new Promise((resolve, reject) => reject(reason))
  • all
static all = promises => {
    if (!isArrayLikeObject(promises)) {
      throw new TypeError(
        `${
          typeof promises === 'undefined' ? '' : typeof promises
        } ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`
      )
    }
    
    // 實(shí)現(xiàn)的 promise 基于 macroTask 的 setTimeout 實(shí)現(xiàn)崎场,需要 async/await 調(diào)節(jié)執(zhí)行順序
    // 原生的 promise 基于 microTask 實(shí)現(xiàn),執(zhí)行順序是正確的遂蛀,不需要 async/await
    return new Promise(async (resolve, reject) => {
      const result = []

      for (const promise of promises) {
        await Promise.resolve(promise).then(resolvePromise, rejectPromise)
      }

      return resolve(result)

      function resolvePromise(value) {
        if (value instanceof Promise) {
          value.then(resolvePromise, rejectPromise)
        } else {
          result.push(value)
        }
      }

      function rejectPromise(reason) {
        return reject(reason)
      }
    })
}
  • race
static race = promises => {
    if (!isArrayLikeObject(promises)) {
      throw new TypeError(
        `${
          typeof promises === 'undefined' ? '' : typeof promises
        } ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`
      )
    }
    
    return new Promise((resolve, reject) => {
      for (const promise of promises) {
        Promise.resolve(promise).then(
          value => resolve(value),
          reason => reject(reason)
        )
      }
    })
}

4谭跨、加入一些健壯性代碼

這一部分基本上屬于修修補(bǔ)補(bǔ)了,加強(qiáng) Promise 的健壯性

  • 校驗(yàn) executor
constructor(executor) {
    // 參數(shù)校驗(yàn)
    if (typeof executor !== 'function') {
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
}
  • 利用 Maybe函子 的思想李滴,校驗(yàn) onFulfilledonRejected
then = (onFulfilled, onRejected) => {
    // 如果 onFulfilled 不是函數(shù)螃宙,其必須被“忽略”
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : value => value

    // 如果 onFulfilled 不是函數(shù),其必須被“忽略”
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : error => {
            throw error
          }
}

五所坯、裝修為 Typescript 風(fēng)格

這一部分就不寫上來了缤谎,repo 里有記錄帖努。

六、測(cè)試是否符合 Promise/A+ 規(guī)范

我們通過一個(gè)庫來檢測(cè)寫好的 Promise

添加需要的膠水代碼:

class Promise {
    ......
    static defer = () => {
        let dfd: any = {}
        dfd.promise = new Promise((resolve, reject) => {
          dfd.resolve = resolve
          dfd.reject = reject
        })
        return dfd
    }

    static deferred = Promise.defer
    ......
}
npm i promises-aplus-tests -D

npx promises-aplus-tests promise.js
QQ20191215-201043.png

總結(jié)

最近在翻閱資料的過程中毫玖,真實(shí)地感悟到什么是“溫故而知新”和“前端的知識(shí)雖然雜但是都有聯(lián)系”着降。本來 Promise 的實(shí)現(xiàn)都被寫爛了辜荠,但是在學(xué)習(xí)函數(shù)式編程的時(shí)候居然又繞回來了瘤缩,這種感覺實(shí)在奇妙紊服,讓人不禁佩服第一個(gè)產(chǎn)生 Promise 想法的人。Promise 將 函子(functor)觀察者模式
相結(jié)合蒙谓,加以 狀態(tài) 斥季、Promise 的解決過程 進(jìn)行改造,最終得以實(shí)現(xiàn)一個(gè)異步解決方案累驮。

篇幅有限泻肯,難免一些錯(cuò)誤渊迁,歡迎探討和指教~
附一個(gè) GitHub 完整的 repo 地址:https://github.com/LazyDuke/ts-promise-from-functor-observer

后記

這是一個(gè)系列慰照,系列文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毒租,一起剝皮案震驚了整個(gè)濱河市稚铣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌墅垮,老刑警劉巖惕医,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異算色,居然都是意外死亡抬伺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門灾梦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峡钓,“玉大人,你說我怎么就攤上這事若河∧苎遥” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵萧福,是天一觀的道長拉鹃。 經(jīng)常有香客問我,道長鲫忍,這世上最難降的妖魔是什么膏燕? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮悟民,結(jié)果婚禮上坝辫,老公的妹妹穿的比我還像新娘。我一直安慰自己逾雄,他們只是感情好阀溶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸦泳,像睡著了一般银锻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上做鹰,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天击纬,我揣著相機(jī)與錄音,去河邊找鬼钾麸。 笑死更振,一個(gè)胖子當(dāng)著我的面吹牛炕桨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肯腕,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼献宫,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了实撒?” 一聲冷哼從身側(cè)響起姊途,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎知态,沒想到半個(gè)月后捷兰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡负敏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年贡茅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片其做。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顶考,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庶柿,到底是詐尸還是另有隱情村怪,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布浮庐,位于F島的核電站甚负,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏审残。R本人自食惡果不足惜梭域,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搅轿。 院中可真熱鬧病涨,春花似錦、人聲如沸璧坟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雀鹃。三九已至幻工,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間黎茎,已是汗流浹背囊颅。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踢代。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓盲憎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胳挎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饼疙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355