Nodejs Promise 讀書筆記

Nodejs Promise 讀書筆記

前言

Promise是抽象異步處理對象以及對其進行各種操作的組件爬虱。(Promise并不是從JavaScript中發(fā)源的概念)伦腐。簡單說就是一個容器辟狈, 里面保存著某個未來才會結(jié)束的事件( 通常是一個異步操作)的結(jié)果藤韵。

JavaScript中是通過回調(diào)函數(shù)來處理異步邏輯的,比如讀取文件的代碼,如下所示

getAsync("fileA.txt", function(error, result){
    if(error){
        // 取得失敗時的處理 throw error;
    }
}

Nodejs中規(guī)定在Javascript的回掉函數(shù)的第一個參數(shù)是Error對象稠肘。像上面這樣基于回調(diào)函數(shù)的異步處理如果統(tǒng)一參數(shù)使用規(guī)則的話,寫法也會很明了萝毛。但是项阴,這也僅是編碼規(guī)約而已,即使采用不同的寫法也不會出錯笆包。

Promise則是把類似的異步處理對象和處理規(guī)則進行規(guī)范化环揽,并按照采用統(tǒng)一的接口來編寫,而采取規(guī)定方法之外的寫法都會出錯庵佣。

下面通過Promise寫法改寫上面的函數(shù)

var promise = getAsyncPromise("fileA.txt"); //返回promise對象
promise.then(function(result){
    // 獲取文件內(nèi)容成功時的處理
}).catch(function(error){
    // 獲取文件內(nèi)容失敗時的處理
});

我們可以向這個預(yù)設(shè)了抽象化異步處理的promise對象歉胶,注冊這個promise對象執(zhí)行成功 時和失敗時相應(yīng)的回調(diào)函數(shù)。

這和回調(diào)函數(shù)方式相比有哪些不同之處呢? 在使用promise進行一步處理的時候巴粪,我們 必須按照接口規(guī)定的方法編寫處理代碼通今。

也就是說粥谬,除promise對象規(guī)定的方法(這里的 thencatch )以外的方法都是不可以使用的,而不會像回調(diào)函數(shù)方式那樣可以自己自由的定義回調(diào)函數(shù)的參數(shù)辫塌,而必須嚴格遵守固定漏策、統(tǒng)一的編程方式來編寫代碼。

這樣臼氨,基于Promise的統(tǒng)一接口的做法掺喻, 就可以形成基于接口的各種各樣的異步處理模 式。所以一也,promise的功能是可以將復(fù)雜的異步處理輕松地進行模式化巢寡,這也可以說得上是 使用promise的理由之一喉脖。

Promise 簡介

構(gòu)造器

要想創(chuàng)建一個Promise對象椰苟,可以使用new調(diào)用Promise的構(gòu)造器來進行實例化。

var promise = new Promise(function(resolve, reject) { // 異步處理
// 處理結(jié)束后树叽、調(diào)用resolve 或 reject
});

實例方法

Promise.prototype.then

對通過new生成的promise對象為了設(shè)置其值在 resolve (成功)/ reject(失斢吆)時調(diào)用的回調(diào)函數(shù) 可以使用 promise.then()實例方法(也就是說作用是為 Promise 實例添加狀態(tài)改變時的回調(diào)函數(shù)。)题诵。

promise.then(onFulfilled, onRejected)

then方法的第一個參數(shù)是 Resolved 狀態(tài)的回調(diào)函數(shù)洁仗, 第二個參數(shù)( 可選) 是 Rejected 狀態(tài)的回調(diào)函數(shù)。

  • resolve(成功)時 onFulfilled 會被調(diào)用
  • reject(失敗)時 onRejected 會被調(diào)用

onFulfilled 性锭、 onRejected 兩個都為可選參數(shù)赠潦。

then方法返回的是一個新的 Promise 實例( 注意,不是原來那個 Promise 實例)草冈。 因此可以采用鏈式寫法她奥, 即then方法后面再調(diào)用另一個`then方法。

getJSON("/post/1.json") //返回一個Promise對象怎棱,詳見下文
.then(function(post) {  
    return getJSON(post.commentURL);  //返回一個Promise對象
})
.then(function funcA(comments) {  
    console.log("Resolved: ", comments);  
}, function funcB(err) {  
    console.log("Rejected: ", err);  
});  

上面的代碼使用then方法哩俭,依次指定了兩個回調(diào)函數(shù)。 第一個回調(diào)函數(shù)完成以后拳恋,會將返回結(jié)果作為參數(shù)凡资,傳入第二個回調(diào)函數(shù)。采用鏈式的then谬运,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)隙赁。

Promise.prototype.catch()

promise.then成功和失敗時都可以使用。另外在只想對異常進行處理時可以采用Promise.then(undefined, onRejected)這種方式梆暖,只指定reject時的回調(diào)函數(shù)即可伞访。Promise.prototype.catch方法是.then(null, rejection) 的別名, 用于指定發(fā)生錯誤時的回調(diào)函數(shù),等同于拋出錯誤式廷。
上文的代碼可以改造成如下

getJSON("/post/1.json") //返回一個Promise對象咐扭,詳見下文
.then(function(post) {  
    return getJSON(post.commentURL);  //返回一個Promise對象
})
.then(function (comments) {  
    console.log("Resolved: ", comments);  
})
.catch(err) {  
    console.log("Rejected: ", err);  
});  

需要注意的是,如果 Promise 狀態(tài)已經(jīng)變成Resolved, 再拋出錯誤是無效的蝗肪。

var promise = new Promise(function(resolve, reject) {  
    resolve('ok');  
    throw new Error('test');  
});  
promise  
    .then(function(value) {  
        console.log(value)  
    })  
    .catch(function(error) {  
        console.log(error)  
    });  
// ok 

上面代碼中袜爪, Promise 在resolve語句后面,再拋出錯誤薛闪,不會被捕獲辛馆, 等于沒有拋出。

Promise 對象的錯誤具有“ 冒泡” 性質(zhì)豁延, 會一直向后傳遞昙篙, 直到被捕獲為止。 也就是說诱咏, 錯誤總是會被下一個catch語句捕獲苔可。

var catchTest = new Promise(function(resolve, reject) {  
    setTimeout(function(){
        resolve('aa')
    }, 1000)  
}) 

catchTest
.then(function(value){
    console.log('a')
})
.then(function(value){
    throw new Error('test');  
    console.log('b')
})
.then(function(value){
    console.log('c')
})
.catch(function(error){
    console.log(error)
})

//a
//[Error: test]

上面代碼中,一共有四個Promise 對象:一個由'catchTest'產(chǎn)生袋狞, 三個由then產(chǎn)生焚辅。它們之中的第二個then方法出了錯誤拼卵,中斷了下面的then方法酣溃,直接被最后一個catch捕獲。

建議總是使用catch方法昵时, 而不使用then方法的第二個處理錯誤的參數(shù)早处。

跟傳統(tǒng)的try / catch代碼塊不同的是湾蔓,如果沒有使用catch方法指定錯誤處理的回調(diào)函數(shù),Promise 對象拋出的錯誤不會傳遞到外層代碼砌梆, 即不會有任何反應(yīng)默责。

var someAsyncThing = function() {  
    return new Promise(function(resolve, reject) {  
        //  下面一行會報錯,因為 x 沒有聲明  
        resolve(x + 2);  
    });  
};  
someAsyncThing().then(function() {  
    console.log('everything is great');  
});  

上面代碼中么库,someAsyncThing函數(shù)產(chǎn)生的 Promise 對象會報錯傻丝, 但是由于沒有指定catch方法,這個錯誤不會被捕獲诉儒,也不會傳遞到外層代碼葡缰, 導(dǎo)致運行后沒有任何輸出。

注意忱反, Chrome 瀏覽器不遵守這條規(guī)定泛释, 它會拋出錯誤“ ReferenceError: x is not defined”。

var promise = new Promise(function(resolve, reject) {  
    resolve("ok");  
    setTimeout(function() {  
        throw new Error('test')  
    }, 0)  
});  
promise.then(function(value) {  
    console.log(value)  
});  
// ok  
// Uncaught Error: test

上面代碼中温算,Promise指定在下一輪“ 事件循環(huán)” 再拋出錯誤怜校, 結(jié)果由于沒有指定使用try...catch語句,就冒泡到最外層注竿,成了未捕獲的錯誤茄茁。 因為此時魂贬,Promise 的函數(shù)體已經(jīng)運行結(jié)束了, 所以這個錯誤是在Promise函數(shù)體外拋出的裙顽。

Node.js 有一個unhandledRejection事件付燥,專門監(jiān)聽未捕獲的reject錯誤。unhandledRejection事件的監(jiān)聽函數(shù)有兩個參數(shù)愈犹, 第一個是錯誤對象键科, 第二個是報錯的 Promise 實例, 它可以用來了解發(fā)生錯誤的環(huán)境信息漩怎。

process.on('unhandledRejection', function(err, p) {  
    console.error(err.stack)  
}); 

需要注意的是勋颖,catch方法返回的還是一個Promise對象,因此后面還可以接著調(diào)用then方法勋锤。

var someAsyncThing = function() {  
    return new Promise(function(resolve, reject) {  
        //  下面一行會報錯饭玲,因為 x 沒有聲明  
        resolve(x + 2);  
    });  
};  
someAsyncThing()  
    .catch(function(error) {  
        console.log('oh no', error);  
    })  
    .then(function() {  
        console.log('carry on');  
    });  
// oh no [ReferenceError: x is not defined]  
// carry on 

上面代碼運行完catch方法指定的回調(diào)函數(shù),會接著運行后面那個then方法指定的回調(diào)函數(shù)怪得。 如果沒有報錯咱枉, 則會跳過catch方法卑硫。

靜態(tài)方法

像 Promise 這樣的全局對象還擁有一些靜態(tài)方法徒恋。

包括 Promise.all() 還有 Promise.resolve() 等在內(nèi),主要都是一些對Promise進行操作的 輔助方法欢伏。

Promise 狀態(tài)

我們已經(jīng)大概了解了Promise的處理流程入挣,接下來讓我們來稍微整理一下Promise的狀態(tài)。

用 new Promise 實例化的promise對象有以下三個狀態(tài)硝拧。

  • "has-resolution" 即Fulfilled
    resolve(成功)時径筏。此時會調(diào)用 onFulfilled
  • "has-rejection" 即Rejected
    reject(失敗)時。此時會調(diào)用 onRejected
  • "unresolved" 即Pending
    既不是resolve也不是reject的狀態(tài)障陶。也就是promise對象剛被創(chuàng)建后的初始化狀態(tài)等

關(guān)于上面這三種狀態(tài)的讀法滋恬,其中左側(cè)為在 ES6 Promises 規(guī)范中定義的術(shù)語, 而右側(cè)則是在 Promises/A+ 中描述狀態(tài)的術(shù)語抱究。

01.png-250.7kB
01.png-250.7kB

promise對象的狀態(tài)恢氯,從Pending轉(zhuǎn)換為Fulfilled或Rejected之后, 這個promise對象的狀態(tài)就不會再發(fā)生任何變化鼓寺。也就是說勋拟,只有異步操作的結(jié)果可以決定當前是哪一種狀態(tài),其他任何操作都無法改變這種狀態(tài)妈候;一旦狀態(tài)改變敢靡,就不會再改變。

Promise與Event等不同苦银,在 .then 后執(zhí)行的函數(shù)可以肯定地說只會被調(diào)用一次啸胧。

還有需要注意赶站,Promise創(chuàng)建后回立刻執(zhí)行,看下面代碼

var promise = new Promise(function(resolve, reject) {  
    console.log('Promise');  
    resolve();  
});  
promise.then(function() {  
    console.log('Resolved.');  
});  
console.log('Hi!');  
// Promise  
// Hi!  
// Resolved 

上面代碼中, Promise 新建后立即執(zhí)行纺念, 所以首先輸出的是“ Promise”亲怠。 然后, then方法指定的回調(diào)函數(shù)柠辞, 將在當前腳本所有同步任務(wù)執(zhí)行完才會執(zhí)行团秽, 所以“ Resolved” 最后輸出。

Promise也是有缺點的

  • 無法取消Promise叭首,一旦新建它就會立即執(zhí)行习勤,無法中途取消。
  • 如果不設(shè)置回調(diào)函數(shù)焙格, Promise內(nèi)部拋出的錯誤图毕,不會反應(yīng)到外部。
  • 當處于Pending狀態(tài)時眷唉, 無法得知目前進展到哪一個階段(剛剛開始還是即將完成)予颤。

編寫 Promise代碼

下面介紹一下如何編寫一下Promise代碼。

創(chuàng)建Promise對象流程

  1. new Promise(fn)返回一個Promise對象
  2. fn中指定異步等處理邏輯
    ? 處理結(jié)果正常的話冬阳,調(diào)用 resolve(處理結(jié)果值)
    ? 處理結(jié)果錯誤的話蛤虐,調(diào)用 reject(Error對象)

按照這個流程,我們來寫一段promise代碼吧肝陪。任務(wù)是用Promise通過異步處理方式來獲取XMLHttpRequest(XHR)的數(shù)據(jù)驳庭。

創(chuàng)建XHR的promise對象

首先,創(chuàng)建一個用Promise把XHR處理包裝起來的名為 getURL 的函數(shù)氯窍。

function getURL(URL) {
    return new Promise(function (resolve, reject) {
        var req = new XMLHttpRequest(); 

        req.open('GET', URL, true); 

        req.onload = function () {
            if (req.status === 200) { 
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            } 
        };
        
        req.onerror = function () { 
            reject(new Error(req.statusText));
        };
        req.send(); 
    });
}
// 運行示例
var URL = "http://httpbin.org/get"; 
getURL(URL).then(function onFulfilled(value){
    console.log(value); 
}).catch(function onRejected(error){
    console.error(error); 
});

getURL 只有在通過XHR取得結(jié)果狀態(tài)為200時才會調(diào)用 resolve,而其他情況(取得失敗)時則會調(diào)用 reject 方法饲常。

  • resolve(req.responseText)
    resolve函數(shù)的作用是, 將 Promise對象的狀態(tài)從“ 未完成” 變?yōu)椤?成功”( 即從 Pending變?yōu)?code>Resolved)狼讨,在異步操作成功時調(diào)用贝淤,并將異步操作結(jié)果,作為參數(shù)傳遞出去政供。
    參數(shù)并沒有特別的規(guī)則播聪,基本上把要傳給回調(diào)函數(shù)參數(shù)放進去就可以了。 ( then 方法可以接收到這個參數(shù)值)

  • reject(new Error(req.statusText));
    reject函數(shù)的作用是鲫骗,將 Promise 對象的狀態(tài)從“ 未完成” 變?yōu)椤?失敗”( 即從 Pending 變?yōu)?code>Rejected)犬耻,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤执泰,作為參數(shù)傳遞出去枕磁。

    上文中,XHR中 onerror事件被觸發(fā)的時候就是發(fā)生錯誤時术吝,所以理所當然調(diào)用 reject 计济。發(fā)生錯誤時茸苇,創(chuàng)建一個Error對象后再將具體的值傳進去。傳給 的參數(shù)也沒有什么特殊的限制沦寂,一般只要是Error對象(或者 繼承自Error對象)就可以学密。

Promise實例

Promise實現(xiàn)Ajax操作

var getJSON = function(url) {  
    var promise = new Promise(function(resolve, reject) {  
        var client = new XMLHttpRequest();  
        client.open("GET", url);  
        client.onreadystatechange = handler;  
        client.responseType = "json";  
        client.setRequestHeader("Accept", "application/json");  
        client.send();  
  
        function handler() {  
            if(this.readyState !== 4) {  
                return;  
            }  
            if(this.status === 200) {  
                resolve(this.response);  
            } else {  
                reject(new Error(this.statusText));  
            }  
        };  
    });  
    return promise;  
};  
getJSON("/posts.json").then(function(json) {  
    console.log('Contents: ' + json);  
}, function(error) {  
    console.error(' 出錯了 ', error);  
});  

上面代碼中,getJSON是對 XMLHttpRequest 對象的封裝传藏, 用于發(fā)出一個針對 JSON 數(shù)據(jù)的 HTTP 請求腻暮, 并且返回一個 Promise 對象。 需要注意的是毯侦,在getJSON內(nèi)部哭靖, resolve函數(shù)和reject函數(shù)調(diào)用時, 都帶有參數(shù)侈离。關(guān)于參數(shù)傳遞试幽,上文做過簡要介紹

如果調(diào)用resolve函數(shù)和reject函數(shù)時帶有參數(shù),那么它們的參數(shù)會被傳遞給回調(diào)函數(shù)卦碾。
reject函數(shù)的參數(shù)通常是 Error 對象的實例铺坞,表示拋出的錯誤;
resolve函數(shù)的參數(shù)除了正常的值以外洲胖,還可能是另一個 Promise實例济榨, 表示異步操作的結(jié)果有可能是一個值,也有可能是另一個異步操作宾濒,比如像下面這樣腿短。

var p1 = new Promise(function(resolve, reject) {  
    // ...  
});  
var p2 = new Promise(function(resolve, reject) {  
    // ...  
    resolve(p1);  
})

上面代碼中,p1p2都是 Promise 的實例绘梦, 但是p2resolve方法將p1作為參數(shù),即一個異步操作的結(jié)果是返回另一個異步操作赴魁。

注意卸奉,這時p1的狀態(tài)就會傳遞給p2,也就是說颖御,p1的狀態(tài)決定了p2的狀態(tài)榄棵。如果p1的狀態(tài)是Pending,那么p2的回調(diào)函數(shù)就會等待p1的狀態(tài)改變潘拱; 如果p1的狀態(tài)已經(jīng)是Resolved或者Rejected疹鳄, 那么p2的回調(diào)函數(shù)將會立刻執(zhí)行。

var p1 = new Promise(function(resolve, reject) {  
    setTimeout(() => reject(new Error('fail')), 3000)  
})  
var p2 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve(p1), 1000)  
})  
p2.then(result => console.log(result))
.catch(error => console.log(error))

上面代碼中芦岂,p1是一個 Promise瘪弓, 3 秒之后變?yōu)?code>rejected。 p2的狀態(tài)在 1 秒之后改變禽最, resolve方法返回的是p1腺怯。 此時袱饭, 由于p2返回的是另一個Promise, 所以后面的then語句都變成針對后者( p1)呛占。 又過了 2 秒 p1變?yōu)?code>rejected虑乖,導(dǎo)致觸發(fā)catch方法指定的回調(diào)函數(shù)。

使用bluebird爬蟲實踐

推薦文章

關(guān)于Promise 你真的了解多少晾虑?
es6中的Promise
JavaScript Promise 迷你書
中文版 JavaScript Promise 迷你書

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疹味,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帜篇,更是在濱河造成了極大的恐慌佛猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坠狡,死亡現(xiàn)場離奇詭異继找,居然都是意外死亡,警方通過查閱死者的電腦和手機逃沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門婴渡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凯亮,你說我怎么就攤上這事边臼。” “怎么了假消?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵柠并,是天一觀的道長。 經(jīng)常有香客問我富拗,道長臼予,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任啃沪,我火速辦了婚禮粘拾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘创千。我一直安慰自己缰雇,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布追驴。 她就那樣靜靜地躺著械哟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殿雪。 梳的紋絲不亂的頭發(fā)上暇咆,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音冠摄,去河邊找鬼糯崎。 笑死几缭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的沃呢。 我是一名探鬼主播年栓,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼薄霜!你這毒婦竟也來了某抓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惰瓜,失蹤者是張志新(化名)和其女友劉穎否副,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崎坊,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡备禀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奈揍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曲尸。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖男翰,靈堂內(nèi)的尸體忽然破棺而出另患,到底是詐尸還是另有隱情,我是刑警寧澤蛾绎,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布昆箕,位于F島的核電站,受9級特大地震影響租冠,放射性物質(zhì)發(fā)生泄漏鹏倘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一肺稀、第九天 我趴在偏房一處隱蔽的房頂上張望第股。 院中可真熱鬧,春花似錦话原、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至归园,卻和暖如春黄虱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捻浦。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留朱灿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓盗扒,卻偏偏與公主長得像跪楞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侣灶,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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

  • Promiese 簡單說就是一個容器褥影,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果池户,語法上說,Pr...
    雨飛飛雨閱讀 3,358評論 0 19
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗的人凡怎,如果你還沒有使用過Promise校焦,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,310評論 6 19
  • 00、前言Promise 是異步編程的一種解決方案坛缕,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)...
    夜幕小草閱讀 2,133評論 0 12
  • Promise的含義: ??Promise是異步編程的一種解決方案毙沾,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評論 0 16
  • 支教進入倒計時宠页,天氣進入風(fēng)雨季,越發(fā)冷了举户。一早就聽到雨聲,心里第一反應(yīng)是體育課上不成了俭嘁,怎么辦呢?對于我這個只愛...
    國家理財規(guī)劃師孟玲閱讀 199評論 0 1