JavaScript:Promise的基礎套路

關于Promise的基本內(nèi)容初狰,已經(jīng)寫過一篇文章莫杈。基本的會用跷究,偶爾也用過姓迅,不過知識點比較多,并且代碼在外面套了好幾層俊马,感覺也比較復雜丁存,一直理不順頭緒。最近看了下面鏈接的幾篇文章柴我,感覺很不錯解寝,對于Promise基本概念的理解比以前要清晰一點了。
Promise的知識點很多艘儒,這里是一次從多到少的收斂過程聋伦,寫了幾點平時可能會用到的最基礎的用法夫偶。

大白話講解Promise(一)
這篇文章寫得還是比較簡單直接的,對于理解概念很有幫助觉增,推薦好好看看兵拢。

廖雪峰的Promise
這篇文章對于概念的分解還是比較詳細的,很不錯逾礁。里面的例子直接copy到chrome的控制臺會報錯说铃,不過簡單修改一下就可以了。本文的例子基本上都是從這里簡單修改來的嘹履。

適用Promise的三種場景腻扇?

  • 場景1: 級聯(lián)調(diào)用,就是幾個調(diào)用依次發(fā)生的場景砾嫉。這個就是有名的回調(diào)地獄幼苛。通用的套路是,新建一個Promise焕刮,啟動流程舶沿,其他的Promise,放在級聯(lián)的then函數(shù)中济锄。

  • 場景2:幾個異步回調(diào)都成功暑椰,然后再進行下一步操作霍转,比如圖片比較大荐绝,分成幾個小圖下載,然后拼接避消,就是一個典型的場景低滩。這里用Promise.all這個函數(shù)就很方便。

  • 場景3: 幾個異步回調(diào)岩喷,只要有一個成功恕沫,就可以進行下一步。比如像主站和另外兩個備用站點同時請求一張圖片纱意,只要有一個有相應婶溯,其他幾個就可以放棄。這里用Promise.race這個函數(shù)就很方便偷霉。

Promise和回調(diào)地獄是什么關系迄委?

  • 單個的回調(diào)函數(shù)不會形成回調(diào)地獄。串行的回調(diào)类少,也就是上面提到的場景1叙身,層層嵌套,導致代碼越套越深硫狞,結構復雜信轿,才形成了回調(diào)地獄晃痴。

  • Promise并不是替代回調(diào)函數(shù),而是對回調(diào)函數(shù)的一層封裝财忽。比如倘核,有名的setTimeOut函數(shù),Promise并不能替代它即彪,只是對它進行了一層包裝笤虫。

  • 當然,也不是簡單的包裝祖凫,最本質(zhì)的變化琼蚯,就是將對回調(diào)函數(shù)的調(diào)用,修改成了消息發(fā)送惠况。將對結果的處理剝離出去遭庶,交給后續(xù)的對象處理。
    resolve(data); reject(error);這兩個理解為消息發(fā)送函數(shù)更確切一點稠屠。一方面峦睡,將所處的Promise的狀態(tài)由pending改為resolved或者reject。另一方面权埠,生成一個新的Promise榨了,并返回,同時將數(shù)據(jù)作為參數(shù)傳遞出去攘蔽。

  • Promise包裝的函數(shù)龙屉,同步和異步都是可以的,沒有本質(zhì)區(qū)別满俗,按照一套思路去理解就可以了转捕。同步的代碼基本不需要包裝,本來就簡單唆垃,比如用Promise.resolve(初始值)發(fā)起一個流程五芝。大多數(shù)情況,Promise包裝的都是異步對象辕万。本質(zhì)是為了把層層嵌套異步回調(diào)代碼枢步,回調(diào)地獄 callback hell,轉(zhuǎn)變?yōu)榇械逆準秸{(diào)用渐尿。

  • Promise對象新建的時候醉途,狀態(tài)是Pending,可以理解為“正在進行中涡戳,需要等待”结蟋。
    resolve(data);一下,狀態(tài)變成了Resolved渔彰,鏈式調(diào)用的控制權就轉(zhuǎn)移到了下一級的then(data => {callback(data)})函數(shù)中嵌屎。這里就是傳統(tǒng)的回調(diào)函數(shù)執(zhí)行的地方推正。
    reject(error);一下,狀態(tài)變成了Rejected宝惰,鏈式調(diào)用的控制權就轉(zhuǎn)移到了catch(error => { })函數(shù)中植榕,一般建議放在最后面。這里就是集中處理錯誤的地方尼夺。
    所以尊残,不要糾結同步還是異步,將重點放在Promise的對象的狀態(tài)以及鏈式調(diào)用的控制權的轉(zhuǎn)移上面淤堵。

Promise的編程范式:面向?qū)ο?or 函數(shù)式寝衫?

  • 創(chuàng)建Promise對象,需要用到new關鍵字拐邪,并且名字也一般叫做對象慰毅。不過,Promise并不是面向?qū)ο蟮木幊淘祝嗟倪€是函數(shù)式汹胃。范疇或者集合,用類來模擬东臀。如果能夠提供一個靜態(tài)函數(shù)Promise.of來替代new關鍵字着饥,函數(shù)式的味道就更濃厚一點。

  • 鏈式調(diào)用惰赋,比較方便宰掉,要做到這一點,每個函數(shù)谤逼,比如then贵扰,catch等仇穗,都返回Promise對象流部,叫范疇或者集合更確切一點。不過纹坐,每一個Promise都是不同的枝冀,這個符合函數(shù)式編程的習慣:生成新的對象,而不是改變對象本身耘子。之間的聯(lián)系主要是數(shù)據(jù)的傳遞果漾,自身內(nèi)部狀態(tài)的變化。

Promise對異步調(diào)用的常用封裝套路:

function promiseFunction(resolve, reject) {
    let timeOut = Math.random() * 2;
    let flag = (timeOut < 1);
    let callback = () => {
        if (flag) {
            console.log('success...');
            return resolve('data: 200 OK'); // 加個return是個好習慣
        } else {
            console.log('fail...');
            return reject('error: timeout in ' + timeOut + ' seconds.'); // 加個return是個好習慣
        }
    };
    // start process
    console.log('start async function ...');
    setTimeout(callback, (timeOut * 1000));
}

function asyncFunction () {
    let promise = new Promise(promiseFunction);
    return promise;
}
  • 對callback的改造:
    一般的callback谷誓,應該定義對結果的處理過程以及出錯的處理過程绒障。Promise剝離了這些具體的處理過程,改成了發(fā)消息捍歪。成功就發(fā)送resolve(data)户辱;失敗就發(fā)送reject(error);
    這里要注意的一點是,resolve(data);reject(error);才顿,這兩個消息函數(shù)至少要用一個返奉,當然多用是沒關系的,否則流程就啟動不了必逆。
    resolve(data)怠堪;reject(error);之后,流程就交給后面的then或者catch來處理了名眉,這之后的代碼都不會執(zhí)行粟矿。所以resolve(data);reject(error);前面加個return损拢,可以更加明確這種意圖嚷炉,是個好習慣

  • 對流程函數(shù)的封裝:
    一般的異步過程都分為兩步:在主線程發(fā)起異步過程,然后主線程就去做其他事情了探橱;具體的工作一般在工作者線程中執(zhí)行申屹。工作完成后,調(diào)用callback隧膏,通知主線程哗讥,讓主線程拿著結果做想做的事。
    Promise把發(fā)起異步過程胞枕,(這里用setTimeout函數(shù)模擬)杆煞,這個步驟封裝在一個函數(shù)中,(就是Promise構造函數(shù)的executor參數(shù))腐泻,這個函數(shù)格式固定决乎,參數(shù)是resolve, reject,這里用一個名字promiseFunction把他列出來派桩。

  • 函數(shù)式編程范式的封裝:
    函數(shù)式編程一般會簡化為范疇或者集合的操作构诚,數(shù)據(jù)和函數(shù)都包裹在一個集合容器中。
    這里用Promise類的對象來模擬铆惑,這也是導致誤認為面向?qū)ο缶幊痰脑蚍吨觥R怨潭ㄌ茁返暮瘮?shù)(resolve, reject)作為參數(shù),通過Promise構造函數(shù)员魏,用new關鍵字丑蛤,得到了一個promise對象,完成封裝撕阎。
    所以受裹,Promise對象在構建過程中,異步流程就已經(jīng)發(fā)起了虏束,Promise對象的狀態(tài)就是pending===這個也是參考文章大白話講解Promise(一)中提到的注意點
    如果不resolve或者reject一下棉饶,(throw error跟reject是同一個意思)脑慧,Promise對象就一直pending,這個鏈式調(diào)用就一直停著砰盐,動不了闷袒。

  • 接口函數(shù)的封裝:
    這層封裝是從軟件工程的角度,方便使用者使用的角度來做的岩梳。
    函數(shù)式編程用來完成跟界面和業(yè)務無關的具體功能是比較好的囊骤,操作的也是集合。但是一般來說冀值,業(yè)務層用面向?qū)ο蟮哪J竭M行設計的也物,調(diào)用函數(shù)式編程的集合不是很方便。所以列疗,封裝成功能型的函數(shù)滑蚯,(也就是上面的asyncFunction函數(shù)),用起來就比較順手了抵栈。
    當然告材,把生成的Promise對象return出去,是為了方便鏈式調(diào)用古劲。

在實際使用中斥赋,可以寫得簡潔一些,上面的代碼可以精簡如下:

function asyncFunction () {
    return new Promise(function(resolve, reject) {
        let timeOut = Math.random() * 2;
        let flag = (timeOut < 1);
        // start process
        console.log('start async function ...');
        setTimeout(() => {
            if (flag) {
                console.log('success...');
               return  resolve('data: 200 OK'); // 加個return是個好習慣
            } else {
                console.log('fail...');
                return reject('error: timeout in ' + timeOut + ' seconds.'); // 加個return是個好習慣
            }
        }, (timeOut * 1000));
    });
}

Promise簡單使用的套路:

  • 所謂簡單使用产艾,就考慮最簡單的異步調(diào)用疤剑,(a)發(fā)起流程,等結果闷堡;(b)成功隘膘,處理結果;(c)失敗杠览,報錯

  • Promise.prototype.then(callback)就是用來處理成功結果的回調(diào)函數(shù)弯菊,具體的處理過程在這里定義。
    then函數(shù)的第二個參數(shù)可以用來處理出錯結果倦零,不過一般都不用误续。在這里處理錯誤是一種很差的方法。
    then函數(shù)會返回一個Promise對象扫茅。這個前面已經(jīng)提過,這個Promise對象是then函數(shù)內(nèi)部新建的育瓜,和流程發(fā)起的那個Promise對象是不一樣的葫隙。

  • then函數(shù)一般建議寫同步過程,這里是執(zhí)行以往回調(diào)函數(shù)功能的地方躏仇。在流程最后恋脚,把接收到的datareturn回去是個好習慣腺办,萬一后面還有其他的then要用,數(shù)據(jù)data就可以順著節(jié)點傳一下糟描,不至于中斷怀喉。
    return data; 和 return Promise.resolve(data);是等價的,內(nèi)部估計會裝換一下船响。所以本質(zhì)上還是return了一個Promise對象
    如果是異步過程躬拢,建議新建一個Promise對象包裝一下,再return见间,這樣就形成了串行依賴關系聊闯。
    如果什么都不return,那么內(nèi)部會新建一個沒有值的Promise對象米诉,相當于return Promise.resolve(undefined);菱蔬;所以這種情況,鏈式調(diào)用還可以繼續(xù)史侣,但是參數(shù)傳遞會中斷拴泌。

then函數(shù)中一般不建議放異步過程,這樣做會增加理解的難度惊橱。下面這篇文章中就有這樣的例子:
Promise.prototype.then()

  • Promise.prototype.catch()本質(zhì)上是.then(null, rejection)的別名弛针,這里是集中處理錯誤的地方,一般放在鏈式調(diào)用所有then的后面李皇。這樣可以捕獲流程中的所有錯誤削茁,包括主流程的以及后續(xù)then中出現(xiàn)的錯誤。

  • 再簡單的過程掉房,(一般是異步過程茧跋,同步過程也一樣,比如直通)卓囚,也用函數(shù)包一下瘾杭,(對外的接口統(tǒng)一為函數(shù),把Promise對象隱藏起來)哪亿,至少給一個then粥烁,最后跟一個catch。將原來一個整體的異步調(diào)用(流程發(fā)起蝇棉,成功讨阻,失敗)轉(zhuǎn)化成了3級的鏈式調(diào)用篡殷,代碼結構清晰很多钝吮。
    比如上面通過Promise封裝好的異步函數(shù),典型的使用套路如下:

asyncFunction().then((data) => {
    console.log(data);
    return data; // 把數(shù)據(jù)往下傳,是個好習慣
    }).catch((error) => {
    console.log(error);
});

場景1: 級聯(lián)調(diào)用使用的套路:

  • 這就是著名的回調(diào)地獄奇瘦,callback hell棘催,采用Promise包裝之后,可以改為簡潔的鏈式調(diào)用耳标。其實就是用級聯(lián)的then來體現(xiàn)這種級聯(lián)的調(diào)用關系醇坝。
    job1.then(job2).then(job3).catch(handleError);
    其中,job1次坡、job2和job3都是封裝了Promise對象的函數(shù)

  • 注意呼猪,這里的job1、job2和job3都要求是一個參數(shù)的函數(shù)贸毕。因為郑叠,不論是resolve,還是reject明棍,傳值的參數(shù)個數(shù)都只有一個乡革。這個可以聯(lián)想到函數(shù)式編程中的柯里化,每次只傳一個參數(shù)摊腋,簡單直接沸版。
    如果想傳個參數(shù)怎么辦呢?將所有參數(shù)包裝成一個對象就可以了兴蒸,resolve和reject都是可以傳遞對象的视粮,只是個數(shù)規(guī)定為一個而已。不過運算的時候需要解析參數(shù)橙凳,再傳值的時候需要重新組裝參數(shù)蕾殴,相對就麻煩一點了。

  • 靜態(tài)函數(shù)Promise.resolve(data)可以快捷地返回一個Promise對象岛啸,一般可以用在鏈式調(diào)用的開頭钓觉,提供初始值。

  • 一般來說坚踩,在新建的Promise對象中發(fā)起異步流程荡灾,resolve(data)消息發(fā)出之后,then(data => { callback(data) })接收到數(shù)據(jù)data瞬铸,將原先的callback在這里執(zhí)行就好了∨希現(xiàn)在這里不放回調(diào)代碼,而是return一個新的Promise對象嗓节,形成一個依賴鏈荧缘。

下面這個例子,就是先用Promise包裝了一個異步過程赦政,(乘10的函數(shù))胜宇;以及一個同步過程耀怜,(加100的函數(shù))恢着;用隨機數(shù)的方式桐愉,模擬過程失敗的情況。然后通過then函數(shù)級聯(lián)的方式定義依賴過程掰派。最后用catch捕捉過程中遇到的錯誤从诲。

Step1:用Promise封裝過程

// input*10的計算結果; setTimeout模擬異步過程;
function multiply10(input) {
    return new Promise(function (resolve, reject) {
        let temp = Math.random() * 1.2;
        let flag = (temp < 1);
        console.log('calculating ' + input + ' x ' + 10 + '...');
        setTimeout(() => {
            if (flag) {
                return resolve(input * 10);
            } else {
                return reject('multiply error:' + temp);
            }
        }, 500);
    });
}

// input+100的計算結果靡羡;同步過程
function add100(input) {
    return new Promise(function (resolve, reject) {
        let temp = Math.random() * 1.2;
        let flag = (temp < 1);
        console.log('calculating ' + input + ' + ' + 100 + '...');
        if (flag) {
            return resolve(input + 100);
        } else {
            return reject('add error:' + temp);
        }
    });
}

Step2:用then函數(shù)級聯(lián)的方式定義依賴過程:

// 結果是3300系洛,或者報錯
Promise.resolve(32).then(multiply10).then(multiply10).then(add100).then(data => {
    console.log('Got value: ' + data);
    return data;
}).catch(error => {
    console.log(error);
});

// 結果是1160,或者報錯
Promise.resolve(6).then(add100).then(multiply10).then(add100).then(data => {
    console.log('Got value: ' + data);
    return data;
}).catch(error => {
    console.log(error);
});

// ... ... 還能寫出很多的組合情況
  • 一般情況then(data => { callback(data) })函數(shù)的主要工作是接收數(shù)據(jù)略步,然后執(zhí)行原來的回調(diào)函數(shù)描扯。
    這里一般放同步代碼;如果是異步代碼趟薄,就像上面那樣绽诚,可以新建一個Promise對象并返回,形成一個調(diào)用鏈杭煎。

  • 如果既有同步的回調(diào)代碼需要執(zhí)行恩够,又有異步的過程需要包裝鏈接,怎么辦呢羡铲?比如上面的例子蜂桶,增加顯示中間過程的功能。
    可以考慮用兩個級聯(lián)的then函數(shù)分別來做這兩件事也切。

一個then用來執(zhí)行同步的回調(diào)函數(shù)扑媚。這里要注意將要傳遞的data return出去,不然雷恃,整個鏈式調(diào)用參數(shù)傳遞會中斷疆股。

.then(data => { 
    callbacek(data);
    return data;   // 這里要把接收到的data傳出去,不然整個調(diào)用鏈的參數(shù)傳遞會斷掉褂萧。
})

一個then用來包裝異步過程的押桃,并把這個新建的Promise return出去,形成異步過程依賴鏈导犹。

.then(data => { 
    return new Promise(function(resolve, reject) {
        let flag = ((Math.random() * 2) < 1);  // demo flag
        let newData = data + 1; // demo data 
        setTimeout(() => { // demo async function
            if (flag) {
                resolve(newData);
            } else {
                reject(new Error('error message'));
            }
        }, 10);
    });
})
  • 上面的新需求可以按照下面的套路簡單實現(xiàn):
// 結果是9880唱凯,或者報錯
Promise.resolve(888).then(add100).then(data => {
    console.log('add100之后的結果為:' + data);
    return data;
}).then(multiply10).then(data => {
    console.log('multiply10之后的結果為:' + data);
    return data;
}).then(data => {
    console.log('Got value: ' + data);
    return data; // 這里是最后了,不return data對流程沒影響谎痢。不過誰知道以后會不會加新的節(jié)點磕昼,return一下還是好的。
}).catch(error => {
    console.log(error);
});

場景2: 幾個異步回調(diào)都成功节猿,然后再進行下一步操作
場景3: 幾個異步回調(diào)票从,只要有一個成功漫雕,就可以進行下一步

  • 這兩種的實現(xiàn)方式很類似,可以按照一種套路模式

  • 只考慮異步過程峰鄙,不考慮同步過程

  • 這里用到了兩個靜態(tài)函數(shù)浸间,分別是Promise.all(),(場景2)吟榴;Promise.race()魁蒜,(場景3);

  • 這兩個函數(shù)的參數(shù)都是一個數(shù)組吩翻,數(shù)組的成員是Promise對象兜看。

  • 后面跟一個then和catch,就像是普通的使用場景狭瞎。

  • Promise.all()成功時细移,傳遞過來的是一個結果數(shù)組;失敗時熊锭,傳遞過來的是出錯對應的值弧轧。

  • 這里沒有鏈式調(diào)用,一長串的數(shù)據(jù)傳遞球涛,所以這里的函數(shù)的參數(shù)個數(shù)沒有限制劣针。不過,統(tǒng)一為一個是比較好的習慣亿扁。就算沒有參數(shù)捺典,給個空對象也可以,萬一以后要傳呢

  • 大白話講解Promise(一)
    Promise.all()从祝,「誰跑的慢襟己,以誰為準執(zhí)行回調(diào)」;
    Promise.race(),「誰跑的快牍陌,以誰為準執(zhí)行回調(diào)」;
    這個表述還是形象而準確的擎浴。

function asyncFunction1(data = null) {
    return new Promise(function(resolve, reject) {
        let temp = Math.random() * 2;
        let flag = (temp < 1);
        // start process
        console.log('start asyncfunction1 ...');
        setTimeout(() => {
            if (flag) {
                if (data) {
                    return resolve(data);
                } else {
                    return resolve('success: asyncfunction1===');
                }
            } else {
                return reject(`fail:asyncfunction1; temp:${temp}`);
            }
        }, 500);
    });
}

function asyncFunction2(data = null) {
    return new Promise(function(resolve, reject) {
        let temp = Math.random() * 2;
        let flag = (temp < 1);
        // start process
        console.log('start asyncfunction2 ...');
        setTimeout(() => {
            if (flag) {
                if (!data) {
                    return resolve(data);
                } else {
                    return resolve('success: asyncfunction2');
                }
            } else {
                return reject(`fail:asyncfunction2; temp:${temp}`);
            }
        }, 500);
    });
}

// 這里傳過來的是成功結果的數(shù)組
Promise.all([asyncFunction1(), asyncFunction2()]).then(array => {
    console.log(JSON.stringify(array));
    return array; // 這里傳遞的是數(shù)組,比較特殊
}).catch(error => {
    console.log(error);
});

// 結果是success: asyncfunction1毒涧;跑得比較快
Promise.race([asyncFunction1(), asyncFunction2()]).then(data => {
    console.log(data);
    return data;
}).catch(error => {
    console.log(error);
});

done贮预、finally、success契讲、fail等其他內(nèi)容呢仿吞?

  • 這些一些框架提供的便利方法,當然捡偏,如果有需要唤冈,也可以自己實現(xiàn)。

  • 上面這些是基本的使用套路银伟,簡單直接你虹。一個基礎應用加三個典型場景绘搞,可以應付平時大多數(shù)的異步過程。

  • 當然傅物,Promise還有很多高級而靈活的用法夯辖。下面推薦幾篇文章,里面的內(nèi)容很豐富挟伙。

Promise 對象(阮一峰)

JavaScript Promise迷你書(中文版)

Promise MDN

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末楼雹,一起剝皮案震驚了整個濱河市模孩,隨后出現(xiàn)的幾起案子尖阔,更是在濱河造成了極大的恐慌,老刑警劉巖榨咐,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件介却,死亡現(xiàn)場離奇詭異,居然都是意外死亡块茁,警方通過查閱死者的電腦和手機齿坷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來数焊,“玉大人永淌,你說我怎么就攤上這事∨宥” “怎么了遂蛀?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長干厚。 經(jīng)常有香客問我李滴,道長,這世上最難降的妖魔是什么蛮瞄? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任所坯,我火速辦了婚禮,結果婚禮上挂捅,老公的妹妹穿的比我還像新娘芹助。我一直安慰自己,他們只是感情好闲先,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布状土。 她就那樣靜靜地躺著,像睡著了一般饵蒂。 火紅的嫁衣襯著肌膚如雪声诸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天退盯,我揣著相機與錄音彼乌,去河邊找鬼泻肯。 笑死,一個胖子當著我的面吹牛慰照,可吹牛的內(nèi)容都是我干的灶挟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼毒租,長吁一口氣:“原來是場噩夢啊……” “哼稚铣!你這毒婦竟也來了?” 一聲冷哼從身側響起墅垮,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤惕医,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后算色,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抬伺,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年灾梦,在試婚紗的時候發(fā)現(xiàn)自己被綠了峡钓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡若河,死狀恐怖能岩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萧福,我是刑警寧澤拉鹃,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站统锤,受9級特大地震影響毛俏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饲窿,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一煌寇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逾雄,春花似錦阀溶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至做鹰,卻和暖如春击纬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钾麸。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工更振, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炕桨,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓肯腕,卻偏偏與公主長得像献宫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子实撒,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344