成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6 系列之我們來聊聊 Promise

MycLambert / 1742人閱讀

前言

Promise 的基本使用可以看阮一峰老師的 《ECMAScript 6 入門》。

我們來聊點(diǎn)其他的。

回調(diào)

說起 Promise,我們一般都會(huì)從回調(diào)或者回調(diào)地獄說起,那么使用回調(diào)到底會(huì)導(dǎo)致哪些不好的地方呢?

1. 回調(diào)嵌套

使用回調(diào),我們很有可能會(huì)將業(yè)務(wù)代碼寫成如下這種形式:

doA( function(){
    doB();

    doC( function(){
        doD();
    } )

    doE();
} );

doF();

當(dāng)然這是一種簡化的形式,經(jīng)過一番簡單的思考,我們可以判斷出執(zhí)行的順序?yàn)椋?/p>

doA()
doF()
doB()
doC()
doE()
doD()

然而在實(shí)際的項(xiàng)目中,代碼會(huì)更加雜亂,為了排查問題,我們需要繞過很多礙眼的內(nèi)容,不斷的在函數(shù)間進(jìn)行跳轉(zhuǎn),使得排查問題的難度也在成倍增加。

當(dāng)然之所以導(dǎo)致這個(gè)問題,其實(shí)是因?yàn)檫@種嵌套的書寫方式跟人線性的思考方式相違和,以至于我們要多花一些精力去思考真正的執(zhí)行順序,嵌套和縮進(jìn)只是這個(gè)思考過程中轉(zhuǎn)移注意力的細(xì)枝末節(jié)而已。

當(dāng)然了,與人線性的思考方式相違和,還不是最糟糕的,實(shí)際上,我們還會(huì)在代碼中加入各種各樣的邏輯判斷,就比如在上面這個(gè)例子中,doD() 必須在 doC() 完成后才能完成,萬一 doC() 執(zhí)行失敗了呢?我們是要重試 doC() 嗎?還是直接轉(zhuǎn)到其他錯(cuò)誤處理函數(shù)中?當(dāng)我們將這些判斷都加入到這個(gè)流程中,很快代碼就會(huì)變得非常復(fù)雜,以至于無法維護(hù)和更新。

2. 控制反轉(zhuǎn)

正常書寫代碼的時(shí)候,我們理所當(dāng)然可以控制自己的代碼,然而當(dāng)我們使用回調(diào)的時(shí)候,這個(gè)回調(diào)函數(shù)是否能接著執(zhí)行,其實(shí)取決于使用回調(diào)的那個(gè) API,就比如:

// 回調(diào)函數(shù)是否被執(zhí)行取決于 buy 模塊
import {buy} from "./buy.js";

buy(itemData, function(res) {
    console.log(res)
});

對于我們經(jīng)常會(huì)使用的 fetch 這種 API,一般是沒有什么問題的,但是如果我們使用的是第三方的 API 呢?

當(dāng)你調(diào)用了第三方的 API,對方是否會(huì)因?yàn)槟硞€(gè)錯(cuò)誤導(dǎo)致你傳入的回調(diào)函數(shù)執(zhí)行了多次呢?

為了避免出現(xiàn)這樣的問題,你可以在自己的回調(diào)函數(shù)中加入判斷,可是萬一又因?yàn)槟硞€(gè)錯(cuò)誤這個(gè)回調(diào)函數(shù)沒有執(zhí)行呢?
萬一這個(gè)回調(diào)函數(shù)有時(shí)同步執(zhí)行有時(shí)異步執(zhí)行呢?

我們總結(jié)一下這些情況:

回調(diào)函數(shù)執(zhí)行多次

回調(diào)函數(shù)沒有執(zhí)行

回調(diào)函數(shù)有時(shí)同步執(zhí)行有時(shí)異步執(zhí)行

對于這些情況,你可能都要在回調(diào)函數(shù)中做些處理,并且每次執(zhí)行回調(diào)函數(shù)的時(shí)候都要做些處理,這就帶來了很多重復(fù)的代碼。

回調(diào)地獄

我們先看一個(gè)簡單的回調(diào)地獄的示例。

現(xiàn)在要找出一個(gè)目錄中最大的文件,處理步驟應(yīng)該是:

fs.readdir 獲取目錄中的文件列表;

循環(huán)遍歷文件,使用 fs.stat 獲取文件信息

比較找出最大文件;

以最大文件的文件名為參數(shù)調(diào)用回調(diào)。

代碼為:

var fs = require("fs");
var path = require("path");

function findLargest(dir, cb) {
    // 讀取目錄下的所有文件
    fs.readdir(dir, function(er, files) {
        if (er) return cb(er);

        var counter = files.length;
        var errored = false;
        var stats = [];

        files.forEach(function(file, index) {
            // 讀取文件信息
            fs.stat(path.join(dir, file), function(er, stat) {

                if (errored) return;

                if (er) {
                    errored = true;
                    return cb(er);
                }

                stats[index] = stat;

                // 事先算好有多少個(gè)文件,讀完 1 個(gè)文件信息,計(jì)數(shù)減 1,當(dāng)為 0 時(shí),說明讀取完畢,此時(shí)執(zhí)行最終的比較操作
                if (--counter == 0) {

                    var largest = stats
                        .filter(function(stat) { return stat.isFile() })
                        .reduce(function(prev, next) {
                            if (prev.size > next.size) return prev
                            return next
                        })

                    cb(null, files[stats.indexOf(largest)])
                }
            })
        })
    })
}

使用方式為:

// 查找當(dāng)前目錄最大的文件
findLargest("./", function(er, filename) {
    if (er) return console.error(er)
    console.log("largest file was:", filename)
});

你可以將以上代碼復(fù)制到一個(gè)比如 index.js 文件,然后執(zhí)行 node index.js 就可以打印出最大的文件的名稱。

看完這個(gè)例子,我們再來聊聊回調(diào)地獄的其他問題:

1.難以復(fù)用

回調(diào)的順序確定下來之后,想對其中的某些環(huán)節(jié)進(jìn)行復(fù)用也很困難,牽一發(fā)而動(dòng)全身。

舉個(gè)例子,如果你想對 fs.stat 讀取文件信息這段代碼復(fù)用,因?yàn)榛卣{(diào)中引用了外層的變量,提取出來后還需要對外層的代碼進(jìn)行修改。

2.堆棧信息被斷開

我們知道,JavaScript 引擎維護(hù)了一個(gè)執(zhí)行上下文棧,當(dāng)函數(shù)執(zhí)行的時(shí)候,會(huì)創(chuàng)建該函數(shù)的執(zhí)行上下文壓入棧中,當(dāng)函數(shù)執(zhí)行完畢后,會(huì)將該執(zhí)行上下文出棧。

如果 A 函數(shù)中調(diào)用了 B 函數(shù),JavaScript 會(huì)先將 A 函數(shù)的執(zhí)行上下文壓入棧中,再將 B 函數(shù)的執(zhí)行上下文壓入棧中,當(dāng) B 函數(shù)執(zhí)行完畢,將 B 函數(shù)執(zhí)行上下文出棧,當(dāng) A 函數(shù)執(zhí)行完畢后,將 A 函數(shù)執(zhí)行上下文出棧。

這樣的好處在于,我們?nèi)绻袛啻a執(zhí)行,可以檢索完整的堆棧信息,從中獲取任何我們想獲取的信息。

可是異步回調(diào)函數(shù)并非如此,比如執(zhí)行 fs.readdir 的時(shí)候,其實(shí)是將回調(diào)函數(shù)加入任務(wù)隊(duì)列中,代碼繼續(xù)執(zhí)行,直至主線程完成后,才會(huì)從任務(wù)隊(duì)列中選擇已經(jīng)完成的任務(wù),并將其加入棧中,此時(shí)棧中只有這一個(gè)執(zhí)行上下文,如果回調(diào)報(bào)錯(cuò),也無法獲取調(diào)用該異步操作時(shí)的棧中的信息,不容易判定哪里出現(xiàn)了錯(cuò)誤。

此外,因?yàn)槭钱惒降木壒?,使?try catch 語句也無法直接捕獲錯(cuò)誤。

(不過 Promise 并沒有解決這個(gè)問題)

3.借助外層變量

當(dāng)多個(gè)異步計(jì)算同時(shí)進(jìn)行,比如這里遍歷讀取文件信息,由于無法預(yù)期完成順序,必須借助外層作用域的變量,比如這里的 count、errored、stats 等,不僅寫起來麻煩,而且如果你忽略了文件讀取錯(cuò)誤時(shí)的情況,不記錄錯(cuò)誤狀態(tài),就會(huì)接著讀取其他文件,造成無謂的浪費(fèi)。此外外層的變量,也可能被其它同一作用域的函數(shù)訪問并且修改,容易造成誤操作。

之所以多帶帶講講回調(diào)地獄,其實(shí)是想說嵌套和縮進(jìn)只是回調(diào)地獄的一個(gè)梗而已,它導(dǎo)致的問題遠(yuǎn)非嵌套導(dǎo)致的可讀性降低而已。

Promise

Promise 使得以上絕大部分的問題都得到了解決。

1. 嵌套問題

舉個(gè)例子:

request(url, function(err, res, body) {
    if (err) handleError(err);
    fs.writeFile("1.txt", body, function(err) {
        request(url2, function(err, res, body) {
            if (err) handleError(err)
        })
    })
});

使用 Promise 后:

request(url)
.then(function(result) {
    return writeFileAsynv("1.txt", result)
})
.then(function(result) {
    return request(url2)
})
.catch(function(e){
    handleError(e)
});

而對于讀取最大文件的那個(gè)例子,我們使用 promise 可以簡化為:

var fs = require("fs");
var path = require("path");

var readDir = function(dir) {
    return new Promise(function(resolve, reject) {
        fs.readdir(dir, function(err, files) {
            if (err) reject(err);
            resolve(files)
        })
    })
}

var stat = function(path) {
    return new Promise(function(resolve, reject) {
        fs.stat(path, function(err, stat) {
            if (err) reject(err)
            resolve(stat)
        })
    })
}

function findLargest(dir) {
    return readDir(dir)
        .then(function(files) {
            let promises = files.map(file => stat(path.join(dir, file)))
            return Promise.all(promises).then(function(stats) {
                return { stats, files }
            })
        })
        .then(data => {

            let largest = data.stats
                .filter(function(stat) { return stat.isFile() })
                .reduce((prev, next) => {
                    if (prev.size > next.size) return prev
                    return next
                })

            return data.files[data.stats.indexOf(largest)]
        })

}
2. 控制反轉(zhuǎn)再反轉(zhuǎn)

前面我們講到使用第三方回調(diào) API 的時(shí)候,可能會(huì)遇到如下問題:

回調(diào)函數(shù)執(zhí)行多次

回調(diào)函數(shù)沒有執(zhí)行

回調(diào)函數(shù)有時(shí)同步執(zhí)行有時(shí)異步執(zhí)行

對于第一個(gè)問題,Promise 只能 resolve 一次,剩下的調(diào)用都會(huì)被忽略。

對于第二個(gè)問題,我們可以使用 Promise.race 函數(shù)來解決:

function timeoutPromise(delay) {
    return new Promise( function(resolve,reject){
        setTimeout( function(){
            reject( "Timeout!" );
        }, delay );
    } );
}

Promise.race( [
    foo(),
    timeoutPromise( 3000 )
] )
.then(function(){}, function(err){});

對于第三個(gè)問題,為什么有的時(shí)候會(huì)同步執(zhí)行有的時(shí)候回異步執(zhí)行呢?

我們來看個(gè)例子:

var cache = {...};
function downloadFile(url) {
      if(cache.has(url)) {
            // 如果存在cache,這里為同步調(diào)用
           return Promise.resolve(cache.get(url));
      }
     return fetch(url).then(file => cache.set(url, file)); // 這里為異步調(diào)用
}
console.log("1");
getValue.then(() => console.log("2"));
console.log("3");

在這個(gè)例子中,有 cahce 的情況下,打印結(jié)果為 1 2 3,在沒有 cache 的時(shí)候,打印結(jié)果為 1 3 2。

然而如果將這種同步和異步混用的代碼作為內(nèi)部實(shí)現(xiàn),只暴露接口給外部調(diào)用,調(diào)用方由于無法判斷是到底是異步還是同步狀態(tài),影響程序的可維護(hù)性和可測試性。

簡單來說就是同步和異步共存的情況無法保證程序邏輯的一致性。

然而 Promise 解決了這個(gè)問題,我們來看個(gè)例子:

var promise = new Promise(function (resolve){
    resolve();
    console.log(1);
});
promise.then(function(){
    console.log(2);
});
console.log(3);

// 1 3 2

即使 promise 對象立刻進(jìn)入 resolved 狀態(tài),即同步調(diào)用 resolve 函數(shù),then 函數(shù)中指定的方法依然是異步進(jìn)行的。

PromiseA+ 規(guī)范也有明確的規(guī)定:

實(shí)踐中要確保 onFulfilled 和 onRejected 方法異步執(zhí)行,且應(yīng)該在 then 方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行。
Promise 反模式

1.Promise 嵌套

// bad
loadSomething().then(function(something) {
    loadAnotherthing().then(function(another) {
        DoSomethingOnThem(something, another);
    });
});
// good
Promise.all([loadSomething(), loadAnotherthing()])
.then(function ([something, another]) {
    DoSomethingOnThem(...[something, another]);
});

2.斷開的 Promise 鏈

// bad
function anAsyncCall() {
    var promise = doSomethingAsync();
    promise.then(function() {
        somethingComplicated();
    });

    return promise;
}
// good
function anAsyncCall() {
    var promise = doSomethingAsync();
    return promise.then(function() {
        somethingComplicated()
    });
}

3.混亂的集合

// bad
function workMyCollection(arr) {
    var resultArr = [];
    function _recursive(idx) {
        if (idx >= resultArr.length) return resultArr;

        return doSomethingAsync(arr[idx]).then(function(res) {
            resultArr.push(res);
            return _recursive(idx + 1);
        });
    }

    return _recursive(0);
}

你可以寫成:

function workMyCollection(arr) {
    return Promise.all(arr.map(function(item) {
        return doSomethingAsync(item);
    }));
}

如果你非要以隊(duì)列的形式執(zhí)行,你可以寫成:

function workMyCollection(arr) {
    return arr.reduce(function(promise, item) {
        return promise.then(function(result) {
            return doSomethingAsyncWithResult(item, result);
        });
    }, Promise.resolve());
}

4.catch

// bad
somethingAync.then(function() {
    return somethingElseAsync();
}, function(err) {
    handleMyError(err);
});

如果 somethingElseAsync 拋出錯(cuò)誤,是無法被捕獲的。你可以寫成:

// good
somethingAsync
.then(function() {
    return somethingElseAsync()
})
.then(null, function(err) {
    handleMyError(err);
});
// good
somethingAsync()
.then(function() {
    return somethingElseAsync();
})
.catch(function(err) {
    handleMyError(err);
});
紅綠燈問題

題目:紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個(gè)燈不斷交替重復(fù)亮燈?(用 Promse 實(shí)現(xiàn))

三個(gè)亮燈函數(shù)已經(jīng)存在:

function red(){
    console.log("red");
}
function green(){
    console.log("green");
}
function yellow(){
    console.log("yellow");
}

利用 then 和遞歸實(shí)現(xiàn):

function red(){
    console.log("red");
}
function green(){
    console.log("green");
}
function yellow(){
    console.log("yellow");
}

var light = function(timmer, cb){
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            cb();
            resolve();
        }, timmer);
    });
};

var step = function() {
    Promise.resolve().then(function(){
        return light(3000, red);
    }).then(function(){
        return light(2000, green);
    }).then(function(){
        return light(1000, yellow);
    }).then(function(){
        step();
    });
}

step();
promisify

有的時(shí)候,我們需要將 callback 語法的 API 改造成 Promise 語法,為此我們需要一個(gè) promisify 的方法。

因?yàn)?callback 語法傳參比較明確,最后一個(gè)參數(shù)傳入回調(diào)函數(shù),回調(diào)函數(shù)的第一個(gè)參數(shù)是一個(gè)錯(cuò)誤信息,如果沒有錯(cuò)誤,就是 null,所以我們可以直接寫出一個(gè)簡單的 promisify 方法:

function promisify(original) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            args.push(function callback(err, ...values) {
                if (err) {
                    return reject(err);
                }
                return resolve(...values)
            });
            original.call(this, ...args);
        });
    };
}

完整的可以參考 es6-promisif

Promise 的局限性 1. 錯(cuò)誤被吃掉

首先我們要理解,什么是錯(cuò)誤被吃掉,是指錯(cuò)誤信息不被打印嗎?

并不是,舉個(gè)例子:

throw new Error("error");
console.log(233333);

在這種情況下,因?yàn)?throw error 的緣故,代碼被阻斷執(zhí)行,并不會(huì)打印 233333,再舉個(gè)例子:

const promise = new Promise(null);
console.log(233333);

以上代碼依然會(huì)被阻斷執(zhí)行,這是因?yàn)槿绻ㄟ^無效的方式使用 Promise,并且出現(xiàn)了一個(gè)錯(cuò)誤阻礙了正常 Promise 的構(gòu)造,結(jié)果會(huì)得到一個(gè)立刻跑出的異常,而不是一個(gè)被拒絕的 Promise。

然而再舉個(gè)例子:

let promise = new Promise(() => {
    throw new Error("error")
});
console.log(2333333);

這次會(huì)正常的打印 233333,說明 Promise 內(nèi)部的錯(cuò)誤不會(huì)影響到 Promise 外部的代碼,而這種情況我們就通常稱為 “吃掉錯(cuò)誤”。

其實(shí)這并不是 Promise 獨(dú)有的局限性,try..catch 也是這樣,同樣會(huì)捕獲一個(gè)異常并簡單的吃掉錯(cuò)誤。

而正是因?yàn)殄e(cuò)誤被吃掉,Promise 鏈中的錯(cuò)誤很容易被忽略掉,這也是為什么會(huì)一般推薦在 Promise 鏈的最后添加一個(gè) catch 函數(shù),因?yàn)閷τ谝粋€(gè)沒有錯(cuò)誤處理函數(shù)的 Promise 鏈,任何錯(cuò)誤都會(huì)在鏈中被傳播下去,直到你注冊了錯(cuò)誤處理函數(shù)。

2. 單一值

Promise 只能有一個(gè)完成值或一個(gè)拒絕原因,然而在真實(shí)使用的時(shí)候,往往需要傳遞多個(gè)值,一般做法都是構(gòu)造一個(gè)對象或數(shù)組,然后再傳遞,then 中獲得這個(gè)值后,又會(huì)進(jìn)行取值賦值的操作,每次封裝和解封都無疑讓代碼變得笨重。

說真的,并沒有什么好的方法,建議是使用 ES6 的解構(gòu)賦值:

Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(([x, y]) => {
    console.log(x, y);
});
3. 無法取消

Promise 一旦新建它就會(huì)立即執(zhí)行,無法中途取消。

4. 無法得知 pending 狀態(tài)

當(dāng)處于 pending 狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段(剛剛開始還是即將完成)。

參考

《你不知道的 JavaScript 中卷》

Promise 的 N 種用法

JavaScript Promise 迷你書

Promises/A+規(guī)范

Promise 如何使用

Promise Anti-patterns

一道關(guān)于Promise應(yīng)用的面試題

ES6 系列

ES6 系列目錄地址:https://github.com/mqyqingfeng/Blog

ES6 系列預(yù)計(jì)寫二十篇左右,旨在加深 ES6 部分知識點(diǎn)的理解,重點(diǎn)講解塊級作用域、標(biāo)簽?zāi)0?、箭頭函數(shù)、Symbol、Set、Map 以及 Promise 的模擬實(shí)現(xiàn)、模塊加載方案、異步處理等內(nèi)容。

如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,請?wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對作者也是一種鼓勵(lì)。

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

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

相關(guān)文章

  • ES6 系列我們聊聊 Async

    摘要:標(biāo)準(zhǔn)引入了函數(shù),使得異步操作變得更加方便。在異步處理上,函數(shù)就是函數(shù)的語法糖。在實(shí)際項(xiàng)目中,錯(cuò)誤處理邏輯可能會(huì)很復(fù)雜,這會(huì)導(dǎo)致冗余的代碼。的出現(xiàn)使得就可以捕獲同步和異步的錯(cuò)誤。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,請?wù)必給予指正,十分感謝。 async ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。 在異步處理上,async 函數(shù)就是 Generator 函數(shù)的語法糖。 ...

    Songlcy 評論0 收藏0
  • ES6 完全使用手冊

    摘要:前言這里的泛指之后的新語法這里的完全是指本文會(huì)不斷更新這里的使用是指本文會(huì)展示很多的使用場景這里的手冊是指你可以參照本文將項(xiàng)目更多的重構(gòu)為語法此外還要注意這里不一定就是正式進(jìn)入規(guī)范的語法。 前言 這里的 ES6 泛指 ES5 之后的新語法 這里的 完全 是指本文會(huì)不斷更新 這里的 使用 是指本文會(huì)展示很多 ES6 的使用場景 這里的 手冊 是指你可以參照本文將項(xiàng)目更多的重構(gòu)為 ES6...

    kgbook 評論0 收藏0
  • ES6 系列我們聊聊裝飾器

    摘要:第二部分源碼解析接下是應(yīng)用多個(gè)第二部分對于一個(gè)方法應(yīng)用了多個(gè),比如會(huì)編譯為在第二部分的源碼中,執(zhí)行了和操作,由此我們也可以發(fā)現(xiàn),如果同一個(gè)方法有多個(gè)裝飾器,會(huì)由內(nèi)向外執(zhí)行。有了裝飾器,就可以改寫上面的代碼。 Decorator 裝飾器主要用于: 裝飾類 裝飾方法或?qū)傩? 裝飾類 @annotation class MyClass { } function annotation(ta...

    eternalshallow 評論0 收藏0
  • 前端進(jìn)階資源整理

    摘要:前端進(jìn)階進(jìn)階構(gòu)建項(xiàng)目一配置最佳實(shí)踐狀態(tài)管理之痛點(diǎn)分析與改良開發(fā)中所謂狀態(tài)淺析從時(shí)間旅行的烏托邦,看狀態(tài)管理的設(shè)計(jì)誤區(qū)使用更好地處理數(shù)據(jù)愛彼迎房源詳情頁中的性能優(yōu)化從零開始,在中構(gòu)建時(shí)間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個(gè)故事講好和 前端進(jìn)階 webpack webpack進(jìn)階構(gòu)建項(xiàng)目(一) Webpack 4 配置最佳實(shí)踐 react Redux狀態(tài)管理之痛點(diǎn)、分析與...

    BlackMass 評論0 收藏0
  • ES6 系列 WeakMap

    摘要:一個(gè)對象若只被弱引用所引用,則被認(rèn)為是不可訪問或弱可訪問的,并因此可能在任何時(shí)刻被回收。也就是說,一旦不再需要,里面的鍵名對象和所對應(yīng)的鍵值對會(huì)自動(dòng)消失,不用手動(dòng)刪除引用。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,請?wù)必給予指正,十分感謝。 前言 我們先從 WeakMap 的特性說起,然后聊聊 WeakMap 的一些應(yīng)用場景。 特性 1. WeakMap 只接受對象作為鍵名 const map = ...

    CollinPeng 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<