亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

[譯] 深入理解 Promise 五部曲:5. LEGO

LiveVideoStack / 2997人閱讀

摘要:一個就像一個樂高玩具。問題是不是你小時候玩兒的那個有趣,它們不是充滿想象力的打氣筒,也不是一種樂高玩具。這是對的并不是給開發(fā)者使用的,它們是給庫作者使用的。不會超過這兩種情況。第二個是根據(jù)第一個處理函數(shù)如何運行來自動變成狀態(tài)成功或者失敗。

原文地址:http://blog.getify.com/promis...

在 Part4:擴展問題 中,我討論了如何擴展和抽象Promise是多么的常見,以及這中間的一些問題。但是為什么promise對于開發(fā)者來說不是足夠友好的呢?這就是它的設(shè)計用意嗎?

I"ve Got Friends In Low Places

Promise被設(shè)計為低級別的構(gòu)建塊。一個promise就像一個樂高玩具。單個樂高只是一個有趣的玩具。但是如果把它們拼在一起,你會感受到更多的樂趣。

問題是promise不是你小時候玩兒的那個有趣LEGO,它們不是充滿想象力的打氣筒,也不是Gandalf mini-figure(一種樂高玩具)。

都不是,promise只是你的簡單老舊的4X2的磚塊。

這并不是使它們非常有用。但是它們是你箱子中最重要的組成部分之一。當(dāng)它們彼此分開時它們只是這么個東西,但是當(dāng)把它們整合在一起它們就會散發(fā)出光芒。

換句話說,promise本質(zhì)上是一個構(gòu)建在真實用戶之上的低級別的API。這是對的:promise并不是給開發(fā)者使用的,它們是給庫作者使用的。

你會從它們那收益許多,但是你很可能不是直接使用它們。你將會使用的是經(jīng)過許多庫組合包裝之后的結(jié)果。

控制 VS 值

請允許我矯正第一個最大的關(guān)于promise的誤解:它們不是真正關(guān)于流程控制的。

promise當(dāng)然可以鏈接在一起來變成近似異步流程控制的東西。但是最后證明它們并不像你想象的那樣擅長這個任務(wù)。promises確實只是一個值的容器。這個值可能現(xiàn)在就存在也可能是未來的一個值。但是不管怎樣,它只是一個值。這是promise最有意義的好處之一。它們在值的上面創(chuàng)建了一個強大的抽象使得值不再是暫存的東西。換句話說,不管那個值現(xiàn)在是否存在,你都可以用同樣的方式使用promise。在這個系列的 第三部分 中,我討論過promise必須是不可變的,它們作為值的意義也是基于這個特點的。

promises就像狀態(tài)的小型的自包含的表現(xiàn)方式。它們是可組合的,也就意味著你全部的程序可以用它們來表示。

限制

就像你不能奢望一個多帶帶的4X2的樂高可以變成一個跑車,讓promise成為你的異步流程控制機制也是一種奢望。
那么promises作為一個非暫存的不可變的值對于解決異步任務(wù)意味著什么呢?在它們設(shè)計哲學(xué)的約束中,有它們擅長并且可以有幫助的東西。

在剩下的內(nèi)容中,我會討論這個限制。但是我并不打算作為一個promise的批判者。我試圖去強調(diào)擴展和抽象的重要性。

錯誤處理

當(dāng)我說promise只是一個值的容器的時候我撒了個小慌。實際上,它是一個成功值或者失敗信息的容器。在任何時候,一個promise是一個未來的成功值或者在獲取這個值時的失敗信息。不會超過這兩種情況。

在某種意義上說,一個promise是一個決策結(jié)構(gòu),一個if..then..else。其他人喜歡把它想成一個try..catch結(jié)構(gòu)。不管是哪種理解,你就像在說"請求一個值,不管成功還是失敗"。

就像尤達說,"Do or do not, there is no try"。

考慮下面這個情況:

function ajax(url) {
    return new Promise( function(resolve,reject){
        // make some ajax request
        // if you get a response, `resolve( answer )`
        // if it fails, `reject( excuses )`
    } );
}

ajax( "http://TheMeaningOfLife.com" )
.then(
    winAtLife,
    keepSearching
);

看到winAtLife()keepSearching()函數(shù)了嗎?我們在說,"去問問生命的意義,不管你有沒有找到答案,我們都繼續(xù)"。

如果我們不傳入keepSearching會怎樣?除了作為一個樂觀主義者假設(shè)你會找到答案然后在生命長河中取勝,這里會有什么危險呢?

如果promise沒有找到生命的意義(或者如果在處理答案的過程中發(fā)生了javascript異常),它會默默地保留著錯誤的事實,也許會永遠保留著。就算你等上一百萬年,你都不會知道對于答案的請求失敗了。

你只能通過觀察才能知道它失敗了。這可能需要深入到形而上學(xué)或者量子學(xué)的東西。讓我們停止在這吧。

所以不帶失敗處理函數(shù)的promise是一個會默默地失敗的promise。這并不好。這意味著如果你忘記了,你會陷入失敗的陷阱而不是成功。

所以你會懷疑:為什么promises會忽略失敗處理函數(shù)呢?因為你可能現(xiàn)在不在意失敗的情況,只有以后某個時刻會關(guān)心。我們程序的暫時性意味著系統(tǒng)現(xiàn)在不會知道你以后會想做什么。現(xiàn)在忽略失敗處理函數(shù)也許對你來說是正合適的,因為你知道你會把這個promise鏈接到另一個promise,并且那個promise有一個失敗處理函數(shù)。

所以promise機制讓你可以創(chuàng)建不需要監(jiān)聽失敗的promise。

這里有一個很微妙的問題,很可能也是大多數(shù)剛接觸promise的開發(fā)者會碰到的問題。

束縛我們的鏈子

為了理解這個問題,我們首先需要理解promises是如何鏈接在一起的。我認為你會很快明白promise鏈?zhǔn)菑姶蟛⑶矣幸稽c復(fù)雜的。

ajax( "http://TheMeaningOfLife.com" )
.then(
    winAtLife,
    keepSearching
)
// a second promise returned here that we ignored!
;

ajax(..)調(diào)用產(chǎn)生了第一個promise,然后then(..)調(diào)用產(chǎn)生了第二個promise。我們沒有捕捉并且觀察在這段代碼中的第二個promise,但是我們可以。第二個promise是根據(jù)第一個promise處理函數(shù)如何運行來自動變成fulfilled狀態(tài)(成功或者失敗)。

第二個promise不會在意第一個promise是成功還是失敗。它在意第一個promise的處理函數(shù)(不管成功還是失敗)。
這是promise鏈的關(guān)鍵。但是這有一點不好理解,所以重復(fù)讀上面那段話直到你理解為止。

考慮下promise代碼通常是怎么寫的(通過鏈):

ajax( ".." )
.then( transformResult )
.then(
    displayAnswer,
    reportError
);

這段代碼也可以像下面這么寫,效果是一樣的:

var promiseA = ajax( ".." );

var promiseB = promiseA.then( transformResult );

var promiseC = promiseB.then(
    displayAnswer,
    reportError
);

// we don"t use `promiseC` here, but we could...

Promise A是唯一在意ajax(..)結(jié)果的promise。

Promise B只關(guān)心Promise A在transformResult(..)函數(shù)內(nèi)部是如何處理的(不是Promise A的結(jié)果本身),同樣的,Promise C只關(guān)心Promise B在displayAnswer(..)或者reportError(..)函數(shù)內(nèi)部是如何處理的(不是Promise B結(jié)果本身)。

再一次,重復(fù)讀這段話直到理解。

transformResult(..)內(nèi)部,如果它立刻完成了它的任務(wù),然后Promise B就會立刻完成,不管成功還是失敗。然而,如果transformResult(..)不能立刻完成,而是創(chuàng)建它自己的promise,我們稱它為Promise H1("H"是"hidden",因為它是隱藏在內(nèi)部的)。原本Promise B返回的等待我們?nèi)绾翁幚鞵romise A的promise,現(xiàn)在概念上被Promise H1替換了(并不是真的替換了,只是被說成一樣的)。

所以,現(xiàn)在當(dāng)你說promiseB.then(..)時,它實際上就像說promiseH1.then(..)。如果Promise H1成功了,displayAnswer(..)會被調(diào)用,但是如果它失敗了,reportError(..)會被調(diào)用。

這就是promise鏈?zhǔn)侨绾喂ぷ鞯摹?/p>

但是,如果Promise A(由ajax調(diào)用返回)失敗了會怎樣?promiseA.then(..)調(diào)用沒有注冊一個失敗處理函數(shù)。它會默默地隱藏錯誤嗎?它會的,除了我們鏈接上Promise B然后在上面注冊一個錯誤處理函數(shù):reportError(..)。如果Promise A失敗了,transformResult(..)不會被調(diào)用,并且沒有錯誤處理函數(shù),所以Promise B馬上被標(biāo)記為失敗,所以reportError(..)會被調(diào)用。

如果Promise A成功了,transformResult(..)會被執(zhí)行,然后當(dāng)運行transformResult(..)時有一個錯誤會怎樣?Promise B被標(biāo)記為失敗,然后reportError(..)也會被調(diào)用。

但是這里是危險的地方,這個地方甚至有經(jīng)驗的開發(fā)者都會遺漏的!

如果Promise A成功了(成功的ajax(..)),然后Promise B成功了(成功的transformResult(..)),但是當(dāng)運行displayAnswer(..)時有一個錯誤會怎樣?

你也許會認為reportError(..)會被調(diào)用?大多數(shù)人會這么想,但是不是的。

為什么?因為來自displayAnswer(..)的一個錯誤或者失敗promise導(dǎo)致一個失敗的Promise C。我們監(jiān)聽Promise C失敗的情況了嗎?仔細看看。沒有。

為了確保你不會漏掉這種錯誤并且讓它默默地隱藏在Promise C狀態(tài)內(nèi)部,你也會希望監(jiān)聽Promise C的失敗:

var promiseC = promiseB.then(
    displayAnswer,
    reportError
);

// need to do this:
promiseC.then( null, reportError );

// or this:, which is the same thing:
promiseC.catch( reportError );

// Note: a silently ignored *Promise D* was created here!

OK,所以現(xiàn)在我們捕獲displayAnswer(..)內(nèi)部的錯誤。不得不去記住這個有一點坑爹。

烏龜

但是有一個更加微妙的問題!如果當(dāng)處理displayAnswer(..)返回的錯誤時,reportError(..)函數(shù)也有一個JS異常會怎樣?會有人捕獲這個錯誤嗎?沒有。

看!上面有一個隱含的Promise D,并且它會被告知reportError(..)內(nèi)部的異常。

OMG,你肯定會想。什么時候才能停止?它會這樣一直下去嗎?

一些promise庫作者認為有必要解決這個問題通過讓"安靜的錯誤"被作為全局異常拋出。但是這種機制該如何得知你不想再鏈接promise并且提供一個錯誤處理函數(shù)呢?它如何知道什么時候應(yīng)該通報一個全局異?;蛘卟煌▓竽兀磕憧隙ú幌M?dāng)你已經(jīng)捕獲并且處理錯誤的情況下仍然有很多控制臺錯誤信息。

在某種意義上,你需要可以標(biāo)記一個promise為“final”,就像說“這是我鏈子中的最后一個promise”或者“我不打算再鏈接了,所以這是烏龜停止的地方”。如果在鏈的最后發(fā)生了錯誤并且沒有被捕獲,然后它需要被報告為一個全局異常。

從表面上我猜測這似乎是很明智的。這種情況下的實現(xiàn)像下面這樣:

var promiseC = promiseB.then(
    displayAnswer,
    reportError
);

promiseC
.catch( reportError )
.done(); // marking the end of the chain

你仍然需要記住調(diào)用done(),要不然錯誤還是會隱藏在最后一個promsie中。你必須使用穩(wěn)固的錯誤處理函數(shù)。
"惡心",你肯定會這么想。歡迎來到promises的歡樂世界。

Value vs Values

對于錯誤處理已經(jīng)說了很多了。另一個核心promsie的限制是一個promise代表一個多帶帶的值。什么是一個多帶帶的值呢?它是一個對象或者一個數(shù)組或者一個字符串或者一個數(shù)字。等等,我還可以在一個容器里放入多個值,就像一個數(shù)組或?qū)ο笾械亩鄠€元素。Cool!

一個操作的最終結(jié)果不總是一個值,但是promise并不會這樣,這很微妙并且又是另一個失敗陷阱:

function ajax(url) {
    return new Promise( function(resolve,reject){
        // make some ajax request
        // if you get a response, `resolve( answer, url )`
        // if it fails, `reject( excuses, url )`
    } );
}

ajax( ".." )
.then(
    function(answer,url){
        console.log( answer, url ); // ..  undefined
    },
    function(excuses,url){
        console.log( excuses, url ); // ..  undefined
    }
);

你看出這里面的問題了嗎?如果你意外的嘗試傳遞超過一個的值過去,不管傳給失敗處理函數(shù)還是成功處理函數(shù),只有第一個值能被傳遞過去,其他幾個會被默默地丟掉。

為什么?我相信這和組合的可預(yù)測性有關(guān),或者一些其他花哨的詞匯有關(guān)。最后,你不得不記住包裹自己的多個值要不然你就會不知不覺的丟失數(shù)據(jù)。

并行

真實世界中的app經(jīng)常在“同一時間”發(fā)生超過一件事情。本質(zhì)上說,我們需要構(gòu)建一個處理器,并行處理多個事件,等待它們?nèi)客瓿稍賵?zhí)行回調(diào)函數(shù)。

相比于promise問題,這是一個異步流程控制的問題。一個多帶帶的promise不能表達兩個或更多并行發(fā)生的異步事件。你需要一個抽象層來處理它。

在計算機科學(xué)術(shù)語中,這個概念叫做一個“門”。一個等待所有任務(wù)完成,并且不關(guān)心它們完成順序的門。

在promise世界中,我們添加一個API叫做Promise.all(..),它可以構(gòu)建一個promise來等待所有傳遞進來的promise完成。

Promise.all([
    // these will all proceed "in parallel"
    makePromise1(),
    makePromise2(),
    makePromise3()
])
.then( .. );

一個相近的方法是race()。它的作用和all()一樣,除了它只要有一個promise返回消息就執(zhí)行回調(diào)函數(shù),而不等待其他promise的結(jié)果。

當(dāng)你思考這些方法的時候,你可能會想到許多方式來實現(xiàn)這些方法。Promise.all(..)Promise.race(..)是原生提供的,因為這兩個方法是很常用到的,但是如果你還需要其他的功能那么你就需要一個庫來幫助你了。限制的另一個表現(xiàn)就是你很快就會發(fā)現(xiàn)你需要自己使用Array的相關(guān)方法來管理promise列表,比如.map(..).reduce(..)。如果你對map/reduce不熟悉,那么趕緊去熟悉一下,因為你會發(fā)現(xiàn)當(dāng)處理現(xiàn)實世界中promise的時候你經(jīng)常會需要它們。

幸運的是,已經(jīng)有很多庫來幫助你了,并且每天還有很多新的庫被創(chuàng)造出來。

Single Shot Of Espresso,Please!

另一個關(guān)于promise的事情是它們只會運行一次,然后就不用了。

如果你只需要處理單個事件,比如初始化一個也沒或者資源加載,那么這樣沒什么問題。但是如果你有一個重復(fù)的事件(比如用戶點擊按鈕),你每次都需要執(zhí)行一系列異步操作會怎么樣呢?Promise并不提供這樣的功能,因為它們是不可變的,也就是不能被重置。要重復(fù)同樣的promise,唯一的方法就是重新定義一個promise。

$("#my_button").click(function(evt){
    doTask1( evt.target )
    .then( doTask2 )
    .then( doTask3 )
    .catch( handleError );
});

太惡心了,不僅僅是因為重復(fù)創(chuàng)建promise對于效率有影響,而且它對于職責(zé)分散不利。你不得不把多個事件監(jiān)聽函數(shù)放在同一個函數(shù)中。如果有一個方式來改變這種情況就好了,這樣事件監(jiān)聽和事件處理函數(shù)就能夠分開了。

Microsoft的RxJS庫把這種方式叫做"觀察者模式"。我的asynquence庫有一個react(..)方法通過簡單的方式提供了一個類似的功能。

盲區(qū)...

在一個已經(jīng)被使用回調(diào)函數(shù)的API占據(jù)的世界中,把promise插入到代碼中比我們想象的要困難。考慮下面這段代碼:

function myAjax(url) {
    return new Promise( function(resolve,reject){
        ajax( url, function(err,response){
            if (err) {
                reject( err );
            }
            else {
                resolve( response );
            }
        } )
    } );
}

我認為promise解決了回調(diào)地獄的問題,但是它們代碼看起來仍然像垃圾。我們需要抽象層來使得用promise表示回調(diào)變得更簡單。原生的promise并沒有提供這個抽象層,所以結(jié)果就是通過原生promise寫出來的代碼還是很丑陋。但是如果有抽象層那么事情就變得很簡單了。

例如,我的asynquence庫提供了一個errfcb()插件(error-first callback),用它可以構(gòu)建一個回調(diào)來處理下面這種場景:

function myAjax(url) {
    var sq = ASQ();
    ajax( url, sq.errfcb() );
    return sq;
}
Stop The Presses!

有時,你想要取消一個promise而去做別的事情,但是如果現(xiàn)在你的promise正處在掛起狀態(tài)會怎樣呢?

var pr = ajax( ".." )
.then( transformResult )
.then(
    displayAnswer,
    reportError
);

// Later
pr.cancel(); //  <-- doesn"t work!

所以,為了取消promise,你需要引入一下東西:

function transformResult(data) {
    if (!pr.ignored) {
        // do something!
    }
}

var pr = ajax( ".." )
.then( transformResult )
.then(
    displayAnswer,
    reportError
);

// Later
pr.ignored = true; // just hacking around

換句話說,你為了能夠取消你的promise,在promise上面加了一層來處理這種情況。你不能從promise取消注冊處理函數(shù)。并且因為一個promise必須不可變,你能夠直接取消一個promise這種情況是不允許出現(xiàn)的。從外部取消一個promise跟改變它的狀態(tài)沒有什么區(qū)別。它使得promise變得不可靠。

許多promise庫都提供了這種功能,但是這明顯是一個錯誤。取消這種行為是不需要promise,但是它可以出現(xiàn)在promise上面的一個抽象層里。

冗長

另一個關(guān)于原生promise的擔(dān)心是有些事情并沒有被實現(xiàn),所以你必須自動手動實現(xiàn)它們,而這些事情對于可擴展性是很重要的,但是這些東西經(jīng)常會導(dǎo)致令人討厭的重復(fù)代碼。

看一個例子,在每一個promise的完成步驟中,有一個設(shè)定就是你希望保持鏈?zhǔn)浇Y(jié)構(gòu),所以then(..)方法會返回一個新的promise。但是如果你想要加入一個自己創(chuàng)建的promise并且從一個成功處理函數(shù)中返回,這樣你的promise就可以加入到鏈的流程控制中。

function transformResult(data) {
    // we have to manually create and return a promise here
    return new Promise( function(resolve,reject){
        // whatever
    } );
}

var pr = ajax( ".." )
.then( transformResult )
.then(
    displayAnswer,
    reportError
);

不同的是,就像上面解釋的一樣,從第一個then(..)返回的隱藏的promise立刻就完成(或者失敗),然后你就沒辦法讓剩下的鏈異步延遲。如果有一個抽象層能夠通過某種方式把自動創(chuàng)建/鏈接的promise暴露給你,然后你就不需要創(chuàng)建自己的promise來替換了,這樣該多好。

換句話說,如果有一個設(shè)定假設(shè)你需要為了異步的目的使用鏈,而不是你只是需要漂亮得執(zhí)行異步。(也就是說你確實是希望你的代碼可以異步執(zhí)行,而不是說希望整個異步流程看過去好看點)。

另一個例子:你不能直接傳遞一個已經(jīng)存在的promise給then(..)方法,你必須傳遞一個返回這個promise的函數(shù)。

var pr = doTask2();

doTask1()
.then( pr ); // would be nice, but doesn"t work!

// instead:

doTask1()
.then( function(){ return pr; } );

這個限制性是有很多原因的。但是它只是減弱了有利于保持可擴展性和可預(yù)測性的用法的簡潔。抽象可以容易的解決這個問題。

全劇終

所有這些原因就是為什么原生的promise API是強大同時也是有局限性的。

關(guān)于擴展和抽象是一個成熟的領(lǐng)域。許多庫正在做這些工作。就像我之前說的,asynquence是我自己的promise抽象庫。它很小但是很強大。它解決了所有博客中提到的promise的問題。

我后面會寫一篇詳細的博客來介紹asynquence是如果解決這些問題的,所以敬請期待。

深入理解Promise五部曲--1.異步問題
深入理解Promise五部曲--2.轉(zhuǎn)換問題
深入理解Promise五部曲--3.可靠性問題
深入理解Promise五部曲--4.擴展性問題
深入理解Promise五部曲--5.樂高問題

最后,安利下我的個人博客,歡迎訪問:http://bin-playground.top

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/87598.html

相關(guān)文章

  • [] 深入理解 Promise 五部:1. 異步問題

    摘要:當(dāng)引擎開始執(zhí)行一個函數(shù)比如回調(diào)函數(shù)時,它就會把這個函數(shù)執(zhí)行完,也就是說只有執(zhí)行完這段代碼才會繼續(xù)執(zhí)行后面的代碼。當(dāng)條件允許時,回調(diào)函數(shù)就會被運行?,F(xiàn)在,返回去執(zhí)行注冊的那個回調(diào)函數(shù)。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關(guān)于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...

    CHENGKANG 評論0 收藏0
  • [] 深入理解 Promise 五部:4. 擴展問題

    摘要:有一個和相關(guān)的更大的問題。最后,請負有責(zé)任感并且使用安全的擴展。深入理解五部曲異步問題深入理解五部曲轉(zhuǎn)換問題深入理解五部曲可靠性問題深入理解五部曲擴展性問題深入理解五部曲樂高問題最后,安利下我的個人博客,歡迎訪問 原文地址:http://blog.getify.com/promis... 現(xiàn)在,我希望你已經(jīng)看過深入理解Promise的前三篇文章了。并且假設(shè)你已經(jīng)完全理解Promises...

    Shimmer 評論0 收藏0
  • 在 PHP 中使用 Promise + co/yield 協(xié)程

    摘要:只要在調(diào)用異步函數(shù)時設(shè)置一個或多個回調(diào)函數(shù),函數(shù)就會在完成時自動調(diào)用回調(diào)函數(shù)。要解決的問題是,如何將回調(diào)方法的參數(shù)從回調(diào)方法中傳遞出來,讓它可以像同步函數(shù)的返回結(jié)果一樣,在回調(diào)函數(shù)以外的控制范圍內(nèi),可以傳遞和復(fù)用。 摘要: 我們知道 JavaScript 自從有了 Generator 之后,就有了各種基于 Generator 封裝的協(xié)程。其中 hprose 中封裝的 Promise 和...

    appetizerio 評論0 收藏0
  • [] 深入理解 Promise 五部:2. 控制權(quán)轉(zhuǎn)換問題

    摘要:直到最近,我們?nèi)匀辉谟煤唵蔚幕卣{(diào)函數(shù)來處理異步的問題。當(dāng)我們只有一個異步任務(wù)的時候使用回調(diào)函數(shù)看起來還不會有什么問題。 原文地址:http://blog.getify.com/promis... 廈門旅行歸來,繼續(xù)理解Promise 在上一篇深入理解Promise五部曲:1.異步問題中,我們揭示了JS的異步事件輪詢并發(fā)模型并且解釋了多任務(wù)是如何相互穿插使得它們看起來像是同時運行的。...

    alanoddsoff 評論0 收藏0
  • [] 深入理解 Promise 五部:3. 可靠性問題

    摘要:簡單的說,即將到來的標(biāo)準(zhǔn)指出是一個,所以作為一個,必須可以被子類化。保護還是子類化這是個問題我真的希望我能創(chuàng)建一個忠實的給及以下。 原文地址:http://blog.getify.com/promis... 如果你需要趕上我們關(guān)于Promise的進度,可以看看這個系列前兩篇文章深入理解Promise五部曲--1.異步問題和深入理解Promise五部曲--2.控制權(quán)轉(zhuǎn)移問題。 Promi...

    XboxYan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<