摘要:說(shuō)明狀態(tài)改變的調(diào)用是同步于的。如果在構(gòu)造函數(shù)的回調(diào)函數(shù)中或的回調(diào)函數(shù)中發(fā)生了異常,返回的會(huì)自動(dòng)。避免了發(fā)送重復(fù)的請(qǐng)求。
什么是Promise
Promise代理了一個(gè)可能要在未來(lái)才能到達(dá)的值[[PromiseValue]]。Promise的一個(gè)最重要的特點(diǎn)是,你可以通過(guò)then來(lái)指定當(dāng)[[PromiseValue]]到來(lái)時(shí)(或到來(lái)失敗時(shí))調(diào)用的handler。
Promise的4種狀態(tài)fulfilled - 成功,[[PromiseValue]]是成功獲取到的值
rejected - 失敗,[[PromiseValue]]是失敗的原因
pending - [[PromiseValue]]還沒(méi)有到達(dá)
settled - [[PromiseValue]]已經(jīng)有結(jié)果(fulfilled或rejected)
創(chuàng)建Promise 方式1:new Promise(executor)new Promise( /* executor */ function(resolve, reject) { ... } );
傳入Promise()的參數(shù)叫做executor,它封裝了獲取[[PromiseValue]]的過(guò)程。
在初始化Promise的過(guò)程中,executor被Promise內(nèi)部代碼執(zhí)行,并給executor傳入2個(gè)參數(shù):resolve函數(shù), reject函數(shù)。
MDN文檔:The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object).
即使在executor中調(diào)用了resolve或reject,也會(huì)先執(zhí)行完當(dāng)前的executor函數(shù)體(不能像return一樣直接退出函數(shù)體)
var p1 = new Promise(function (res, rej) { console.log("before res"); res("ok!"); console.log("after res"); }); console.log("after p1 init, p1:", p1); // before res // after res 它被輸出說(shuō)明:即使在executor中調(diào)用了resolve或reject,也會(huì)先執(zhí)行完當(dāng)前的executor函數(shù)體,而不像return那樣立即退出函數(shù) // after p1 init, p1: Promise { "ok!" } 它在"before/after res"以后才被輸出說(shuō)明:Promise在初始化過(guò)程中就會(huì)同步地調(diào)用executor(called synchronously)
executor的resolve或reject執(zhí)行以后,Promise的狀態(tài)就立刻改變(change synchronously)。
var p1 = new Promise(function (res, rej) { setImmediate(function() { res("haha"); console.log("after res", p1); // executor的res函數(shù)執(zhí)行完畢以后,p1狀態(tài)已經(jīng)變?yōu)閒ulfilled }); }); console.log("after init", p1); // 由于executor的res或rej函數(shù)還未執(zhí)行,p1處于pending狀態(tài) // after init Promise {方式2:Promise.resolve(value)} // after res Promise { "haha" }
Promise.resolve(value);
返回一個(gè)fulfilled的Promise,[[PromiseValue]]為value。
Promise.resolve(promise);
直接返回參數(shù)promise。
Promise.resolve(thenable);
將thenable轉(zhuǎn)換為Promise,Promise的狀態(tài)和[[PromiseValue]]跟隨thenable。thenable會(huì)在后文討論。
方式3:Promise.reject(reason)返回一個(gè)rejected的Promise。
Promise.reject(reason)就是下面代碼的語(yǔ)法糖形式:
new Promise(function(resolve, reject){ reject(reason); });
它沒(méi)有Promise.resolve(something)這么復(fù)雜,不管傳入什么,它直接將參數(shù)reason作為reject原因,返回一個(gè)rejected Promise,即使你閑著沒(méi)事干傳一個(gè)Promise進(jìn)去:
var p = Promise.resolve("res"); Promise.reject(p) .then((val) => { console.log("111", val); }, (err) => { console.log("222", err === p); }); // 222 true
為了方便debug,最好傳入Error實(shí)例。Promise的核心方法:then(onFulfilled[, onRejected]) 先說(shuō)說(shuō)then模式
then模式:你先把成功和失敗時(shí)要調(diào)用的handler傳給then函數(shù),等到時(shí)機(jī)成熟以后(進(jìn)入settled狀態(tài)以后),then函數(shù)就幫你調(diào)用合適的那個(gè)handler。存在一個(gè)這樣的then函數(shù)的對(duì)象叫做Thenable對(duì)象。
// 一個(gè)簡(jiǎn)單的Thenable對(duì)象 var thenable = { then: function (onFulfilled, onRejected) { // setTimeout模擬一個(gè)需要花2秒的異步過(guò)程 setTimeout(function () { var num = Math.random(); if (num > 0.5) { onFulfilled(num); } else { onRejected(num); } }, 2000); } } // 使用方式 thenable.then( function (result) { console.log("get result:", result); }, function (err) { console.log("get error:", err); });
then模式類似于我們經(jīng)常使用的Callback模式。說(shuō)回Promise的then方法
Promise的then方法其實(shí)就是在普通的then模式的基礎(chǔ)上增加了鏈?zhǔn)秸{(diào)用的功能:then函數(shù)返回Promise對(duì)象,前一個(gè)Promise對(duì)象進(jìn)入settled狀態(tài)以后才調(diào)用下一個(gè)then函數(shù)的handler。
Promise.prototype.then()涉及2個(gè)Promise對(duì)象:
調(diào)用then方法的Promise對(duì)象,這里用p1表示
調(diào)用then以后返回的Promise對(duì)象,這里用p2表示
p2 = p1.then(onFulfilled, onRejected);
then的作用就是,立即返回一個(gè)pending狀態(tài)的Promise:p2,并在p1進(jìn)入settled狀態(tài)以后自動(dòng)幫你調(diào)用handler:
如果進(jìn)入fulfilled狀態(tài)(成功),自動(dòng)調(diào)用onFulfilled
如果進(jìn)入rejected狀態(tài)(失敗),自動(dòng)調(diào)用onRejected
在調(diào)用完handler以后,會(huì)根據(jù)handler的返回值觸發(fā)p2的狀態(tài)改變:
如果handler返回一個(gè)普通值val,p2狀態(tài)立即(synchronously)變化:pending-->fulfilled,且p2的[[PromiseValue]]為val。
如果handler中throw一個(gè)錯(cuò)誤err,p2狀態(tài)立即(synchronously)變化:pending-->rejected,且p2的[[PromiseValue]]為err。
如果handler返回一個(gè)settled的Promise對(duì)象temp,p2狀態(tài)立即(synchronously)變化:pending-->與temp相同的狀態(tài),且p2的[[PromiseValue]]與temp的[[PromiseValue]]相同。
如果handler返回一個(gè)pending的Promise對(duì)象temp,p2的狀態(tài)不立即改變,而是等到temp進(jìn)入settled狀態(tài)以后,p2的狀態(tài)再(異步地)改變:pending-->與temp相同的狀態(tài),且p2的[[PromiseValue]]與temp的[[PromiseValue]]相同。
在這里我們只用關(guān)注p2是如何改變的,在后文我會(huì)解釋p2是什么時(shí)候改變的(同步還是異步)以及p2的handler是什么時(shí)候調(diào)用的。
舉個(gè)例子:
Promise.resolve("result").then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
這等價(jià)于:
var p1 = Promise.resolve("result"); var p2 = p1.then(onFulfilled1, onRejected1); var p3 = p2.then(onFulfilled2, onRejected2);
因?yàn)閜1是fulfilled狀態(tài),所以p1的成功handler——onFulfilled1被調(diào)用。
如果onFulfilled1返回普通值(不是Promise),那么p2的狀態(tài)變化:pending-->fulfilled,且p2的[[PromiseValue]]為onFulfilled1的返回值。接下來(lái)p2的成功handler——onFulfilled2被調(diào)用,且傳入onFulfilled2的參數(shù)為p2的[[PromiseValue]]。
如果onFulfilled1返回的是Promise,那么p2的狀態(tài)和[[PromiseValue]]都跟隨這個(gè)被返回的Promise。onFulfilled2將在p2 fulfilled以后被調(diào)用,onRejected2將在p2 rejected以后被調(diào)用,傳入的參數(shù)都是p2的[[PromiseValue]]。
依此類推,p3的狀態(tài)和[[PromiseValue]]都取決于onFulfilled2/onRejected2的返回值。
handler何時(shí)被調(diào)用
handler的調(diào)用異步于then的調(diào)用。
A異步于B的意思:A與B在JavaScript消息隊(duì)列中屬于不同的消息。當(dāng)前消息的調(diào)用棧完全退出以后,Event loop再處理下一個(gè)消息。Event loop處理完B消息以后可能要再處理0個(gè)或多個(gè)消息才能處理到A。
如果p1是通過(guò)new Promise(executor)的方式得到,那么除了滿足第一條以外,p1 handler的調(diào)用還異步于executor中resolve()、reject()的調(diào)用。
如果Promise是由then返回的:
var p1 = Promise.resolve("haha"); var p2 = p1.then(p1_handler); p2.then(p2_handler)
那么除了滿足第一條以外,如我在之前討論then的時(shí)候所說(shuō):
如果p1_handler產(chǎn)生同步的結(jié)果(包括返回普通值、拋出異常、返回settled Promise),則p2狀態(tài)立即變化,且p2_handler立即調(diào)用。這兩者都是同步于p1_handler進(jìn)行的。
如果p1_handler產(chǎn)生異步的結(jié)果(返回pending Promise,temp),則p2狀態(tài)隨著temp自動(dòng)改變,且p2_handler在p2狀態(tài)改變以后自動(dòng)調(diào)用。這兩者不僅異步于p1_handler進(jìn)行,而且異步于temp的狀態(tài)改變。
接下來(lái)我們一個(gè)一個(gè)地討論。
1. handler的調(diào)用異步于then的調(diào)用在調(diào)用then為p1指定handler以后,并不會(huì)立即觸發(fā)handler的調(diào)用,而是向JavaScript消息隊(duì)列中增加一個(gè)消息,然后繼續(xù)執(zhí)行then之后的代碼。等到then所在的執(zhí)行棧完全彈出,Event loop再處理下一個(gè)消息。處理完若干個(gè)消息以后,Event loop處理到handler的消息。處理這個(gè)消息的時(shí)候,先檢查p1是否為settled,如果是,則調(diào)用對(duì)應(yīng)的handler。
即使p1在調(diào)用then時(shí)就是settled狀態(tài),handler的調(diào)用也是異步的。p1在調(diào)用then時(shí)是pending狀態(tài)的話就更不用說(shuō)了。
var p1 = Promise.resolve("haha"); // p1在調(diào)用then時(shí)就是settled狀態(tài) p1.then((val) => { // 在nextTick以后,p1 handler才調(diào)用 console.log("in p1 handler, p1:", p1); }); console.log("after then called, p1:", p1); process.nextTick(() => { console.log("nextTick, p1:", p1); }); // after then called, p1: Promise { "haha" } // nextTick, p1: Promise { "haha" } // in p1 handler, p1: Promise { "haha" }
這也是為什么then返回的p2(在剛被返回的時(shí)候)必定處于pending狀態(tài)。因?yàn)閜1 handler的調(diào)用異步于p1的產(chǎn)生(也就異步于p2的產(chǎn)生)。p2需要等待p1 handler異步調(diào)用并返回結(jié)果才能改變狀態(tài),因此在handler被調(diào)用以前,p2都是pending狀態(tài):
var p1 = Promise.resolve("haha"); console.log("p1", p1); var p2 = p1.then((val) => { console.log("in p1 handler"); return "xixi"; }); console.log("p2", p2); setImmediate(() => { console.log("setImmediate p2", p2); }); // p1 Promise { "haha" } // p2 Promise {2. handler的調(diào)用異步于executor中resolve()、reject()的調(diào)用} // in p1 handler // setImmediate p2 Promise { "xixi" }
如果p1是通過(guò)new Promise(executor)的方式得到,那么除了滿足第一條以外,p1 handler的調(diào)用還異步于executor中resolve()、reject()的調(diào)用。
也就是說(shuō)resolve()、reject()的調(diào)用并不會(huì)立即觸發(fā)handler的調(diào)用,而是向JavaScript消息隊(duì)列中增加一個(gè)消息,等待Event loop處理到這個(gè)消息。處理這個(gè)消息的時(shí)候會(huì)調(diào)用handler。
例子(用node.js運(yùn)行):
var global_val = "old value"; var p1 = new Promise((res) => { setTimeout(function () { res("haha"); console.log(p1); // p1的狀態(tài)立刻改變 console.log("immediately", global_val); // 但是此時(shí)p1的handler還沒(méi)有調(diào)用 process.nextTick(function () { console.log("nextTick", global_val); // 此時(shí)p1的handler還是沒(méi)有調(diào)用 }); setImmediate(function() { console.log("setImmediate", global_val); // 此時(shí)p1的handler已經(jīng)調(diào)用 }); }, 1000); }); var p2 = p1.then(() => { // p1的handler console.log("in p1 handler"); global_val = "new value"; }); // Promise { "haha" } // immediately old value // nextTick old value // in p1 handler // setImmediate new value為什么handler要異步于executor的resolve()、reject()調(diào)用
因?yàn)樵趀xecutor的resolve()、reject()的調(diào)用以后可能還有其他代碼要同步執(zhí)行(當(dāng)前handler還沒(méi)有結(jié)束)。前一個(gè)handler都還沒(méi)有執(zhí)行完,自然不應(yīng)該開(kāi)始下一個(gè)handler的執(zhí)行。(handler的執(zhí)行不應(yīng)該嵌套,而應(yīng)該串行)
比如在上面global_val的例子中,傳入setTimeout的函數(shù)就是一個(gè)handler,調(diào)用res("haha")的時(shí)候這個(gè)handler還有很多代碼要執(zhí)行。那么下一個(gè)handler(p1的handler)不應(yīng)該打斷這些代碼的執(zhí)行。
3. 如果Promise是由then返回的如果Promise是由then返回的:
var p1 = Promise.resolve("haha"); var p2 = p1.then(p1_handler); p2.then(p2_handler)
那么除了滿足第一條以外,如我在之前討論then的時(shí)候所說(shuō):
如果p1_handler產(chǎn)生同步的結(jié)果(包括返回普通值、拋出異常、返回settled Promise),則p2狀態(tài)立即變化,且p2_handler立即調(diào)用。這兩者都是同步于p1_handler進(jìn)行的。
如果p1_handler產(chǎn)生異步的結(jié)果(返回pending Promise,temp),則p2狀態(tài)隨著temp自動(dòng)改變,且p2_handler在p2狀態(tài)改變以后自動(dòng)調(diào)用。這兩者不僅異步于p1_handler進(jìn)行,而且異步于temp的狀態(tài)改變。
這里給出一個(gè)測(cè)試代碼供大家自行驗(yàn)證,注釋中有說(shuō)明,并且可以通過(guò)注釋/解注釋來(lái)修改p1 handler的返回結(jié)果。
// 如果p1_handler產(chǎn)生同步的結(jié)果(包括返回普通值、拋出異常、返回settled Promise),則p2狀態(tài)立即變化,且p2_handler立即調(diào)用。這兩者都是同步于p1_handler進(jìn)行的。 // 如果p1_handler產(chǎn)生異步的結(jié)果(返回pending Promise,temp),則p2狀態(tài)隨著temp自動(dòng)改變,且p2_handler在p2狀態(tài)改變以后自動(dòng)調(diào)用。這兩者不僅異步于p1_handler進(jìn)行,而且異步于temp的狀態(tài)改變。 var p1 = Promise.resolve("haha"); var p2 = p1.then((val) => { console.log("in p1 handler. p1:", p1, "p2", p2, "p3:", p3); process.nextTick(function () { // 當(dāng)p1 handler產(chǎn)生同步的結(jié)果時(shí),p2 handler在nextTick之前就被調(diào)用,且p2在nextTick時(shí)已經(jīng)settled。說(shuō)明p2狀態(tài)改變、p2 handler的調(diào)用是同步于p1 handler的。 // 當(dāng)p1 handler產(chǎn)生異步的結(jié)果時(shí),p2 handler在nextTick之后才被調(diào)用,且p2在nextTick時(shí)依然pending。說(shuō)明p2狀態(tài)改變、p2 handler的調(diào)用是異步于p1 handler的。 console.log("p1 handler nextTick. p1:", p1, "p2", p2, "p3:", p3); }); // throw "err!"; // return Promise.resolve("heihei"); // return Promise.reject("heihei"); // return new Promise(res => { // temp Promise // setTimeout(function () { // res("heihei"); // process.nextTick(function () { // // p2在nextTick時(shí)依然是pending,說(shuō)明p2的狀態(tài)改變異步于temp的狀態(tài)改變 // console.log("resolve nextTick. p1:", p1, "p2", p2, "p3:", p3); // }); // }, 1000); // }); return "heihei"; }); var p3 = p2.then(val => { console.log("in p2 success handler. p1:", p1, "p2", p2, "p3:", p3); return "xixi"; }, err => { console.log("in p2 error handler. p1:", p1, "p2", p2, "p3:", p3); return "hoho"; }); process.nextTick(function () { // 它先于"in p1 handler"輸出可以說(shuō)明handler是異步于then調(diào)用的 console.log("after then called. p1:", p1, "p2", p2, "p3:", p3); });異常處理
Promise rejected 后,將跳至帶有拒絕回調(diào)的下一個(gè) then()(或具有相同功能的 catch())。如果有then(func1, func2),則 func1 或 func2 中的一個(gè)將被調(diào)用,而不可能二者均被調(diào)用。但如果是 then(func1).catch(func2),則有可能兩者均被調(diào)用(func1 rejected時(shí))。
asyncThing1().then(function() { return asyncThing2(); }).then(function() { return asyncThing3(); }).catch(function(err) { return asyncRecovery1(); }).then(function() { return asyncThing4(); }, function(err) { return asyncRecovery2(); }).catch(function(err) { console.log("Don"t worry about it"); }).then(function() { console.log("All done!"); })
藍(lán)線表示 fulfilled 的 promise 路徑,紅路表示 rejected 的 promise 路徑。
如果在promise構(gòu)造函數(shù)的回調(diào)函數(shù)中或then的回調(diào)函數(shù)中發(fā)生了 JavaScript 異常(throw Errow),返回的promise會(huì)自動(dòng)reject。
var jsonPromise = new Promise(function(resolve, reject) { // JSON.parse throws an error if you feed it some // invalid JSON, so this implicitly rejects: resolve(JSON.parse("This ain"t JSON")); }); jsonPromise.then(function(data) { // This never happens: console.log("It worked!", data); }).catch(function(err) { // Instead, this happens: console.log("It failed!", err); })最佳實(shí)踐:緩存Promise——重復(fù)利用異步操作的結(jié)果
var storyPromise; function getChapter(i) { storyPromise = storyPromise || getJSON("story.json"); return storyPromise.then(function(story) { return getJSON(story.chapterUrls[i]); }) } // and using it is simple: getChapter(0).then(function(chapter) { console.log(chapter); return getChapter(1); }).then(function(chapter) { console.log(chapter); })
假設(shè)我們要分別獲取story的各個(gè)章節(jié)(用getChapter函數(shù)),每個(gè)章節(jié)所在的URL存儲(chǔ)在story.json的chapterUrls數(shù)組中。
我們不需要每次要獲取章節(jié)的時(shí)候都先獲取一次story.json在拿到章節(jié)所在URL,更好的做法是:調(diào)用一次getJSON("story.json")就將這個(gè)Promise緩存到storyPromise中,將來(lái)需要請(qǐng)求story.json的時(shí)候只需要重復(fù)利用這個(gè)fulfilled的storyPromise。避免了發(fā)送重復(fù)的HTTP請(qǐng)求。
Promise一旦settled,[[PromiseValue]]不會(huì)再改變,我們可以將它看作一個(gè)定值,多次使用。最佳實(shí)踐:一個(gè) Promise fulfilled 以后再執(zhí)行下一個(gè) Promise
因?yàn)镻romise對(duì)象無(wú)法被外部改變(無(wú)論是意外地還是惡意地),我們可以安全地將這個(gè)對(duì)象交給其他庫(kù)使用,而不用擔(dān)心庫(kù)會(huì)修改到Promise的結(jié)果。
除了手動(dòng)寫(xiě)then回調(diào)來(lái)依次執(zhí)行Promise以外,對(duì)于一個(gè)數(shù)組的任務(wù),我們可以利用array.reduce的循環(huán)來(lái)創(chuàng)建then回調(diào):
// Loop through our chapter urls story.chapterUrls.reduce(function(sequence, chapterUrl) { // 上一個(gè)Promise fulfilled以后才執(zhí)行下一個(gè)getJSON // 從而保證每一個(gè)章節(jié)是順序請(qǐng)求的,從而在頁(yè)面中是順序顯示的 return sequence.then(function() { return getJSON(chapterUrl); }).then(function(chapter) { addHtmlToPage(chapter.html); }); }, Promise.resolve())參考資料
https://developers.google.com...
https://developer.mozilla.org...
http://liubin.org/promises-book/
Node 定時(shí)器詳解講述了process.nextTick()和Promise回調(diào)函數(shù)(microtask)的執(zhí)行順序。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88449.html
摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過(guò)多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門,覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...
摘要:安裝然后在的配置文件加入入口文件引入這樣就可以啦,還是可以減少很多代碼量的。是參數(shù),等同于執(zhí)行正常。這個(gè)包很簡(jiǎn)單,就是引用了和,然后生產(chǎn)環(huán)境把它們編譯到目錄下,做了映射,供使用。 引入 這個(gè)問(wèn)題是對(duì)自己的發(fā)問(wèn),但我相信會(huì)有很多跟我一樣的同學(xué)。對(duì)于 babel 的使用,近半年來(lái)一直停留在與 webpack 結(jié)合使用,以及在瀏覽器開(kāi)發(fā)環(huán)境下。導(dǎo)致很多 babel 的包,我都不清楚他們是干嘛...
摘要:前端工程師自檢清單對(duì)于,掌握其語(yǔ)法和特性是最基本的,但是這些只是應(yīng)用能力,最終仍舊考量仍然是計(jì)算機(jī)體系的理論知識(shí),所以數(shù)據(jù)結(jié)構(gòu),算法,軟件工程,設(shè)計(jì)模式等基礎(chǔ)知識(shí)對(duì)前端工程師同樣重要,這些知識(shí)的理解程度,可以決定你在前端工程師這條路上能走多 2019前端工程師自檢清單 對(duì)于JavaScript,掌握其語(yǔ)法和特性是最基本的,但是這些只是應(yīng)用能力,最終仍舊考量仍然是計(jì)算機(jī)體系的理論知識(shí),所...
摘要:簡(jiǎn)單的說(shuō),即將到來(lái)的標(biāo)準(zhǔn)指出是一個(gè),所以作為一個(gè),必須可以被子類化。保護(hù)還是子類化這是個(gè)問(wèn)題我真的希望我能創(chuàng)建一個(gè)忠實(shí)的給及以下。 原文地址:http://blog.getify.com/promis... 如果你需要趕上我們關(guān)于Promise的進(jìn)度,可以看看這個(gè)系列前兩篇文章深入理解Promise五部曲--1.異步問(wèn)題和深入理解Promise五部曲--2.控制權(quán)轉(zhuǎn)移問(wèn)題。 Promi...
閱讀 2733·2021-11-25 09:43
閱讀 2122·2021-11-24 09:39
閱讀 2051·2021-11-17 09:33
閱讀 2803·2021-09-27 14:11
閱讀 1934·2019-08-30 15:54
閱讀 3262·2019-08-26 18:27
閱讀 1293·2019-08-23 18:00
閱讀 1842·2019-08-23 17:53