解读Promise原理,手动实现一个Promise~

Posted by ssthouse_hust on January 8, 2019

写在最前:

作者:ssthouse_hust 开源中国 授权转载

转载链接:https://my.oschina.net/u/3413999/blog/2988091

~~ 正文从这里开始

首先, 什么是 Promise?

A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred). A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.

关键语句: Promise 是一个在将来某个时刻产生一个单一结果的对象. 通俗一点来说, Promise 代表了一个值, 但是这个值我们并不确定什么时候会被返回.

A promise is an object that may produce a single value some time in the future.

简单看看 Promise 的历史

  • Promise 在 1980 年代被创建出来
  • 在 1988 年正式得名: Promise
  • 已经有很多人了解到了 Promise, 但是人们还是坚持使用 node.js 中提倡的以回调函数首个参数传 error 对象的方式处理异步代码.
  • Dojo 首次大规模的使用了 Promise , 相应的 Promise/A 被提出用以规范 Promise 的实现
  • JQuery 开始使用 Promise 并真正使 Promise 广为人知
  • JQuery 没有实现部分 Promise 的功能, 这也导致了 Promie/A+ 标准的产生
  • ES6 正式引入了 Promise,并且和已有的实现了 Promise/A 规范的 library 相兼容

实现 Promise 之前, 让我们看看 Promise 有哪些规范

  • Promise 是一个 thenable 对象, 也就是说 Promise 有一个 .then() 方法
  • 一个 pending 状态的 Promise 可以进入 fulfilled 和 rejected 状态
  • promise 一旦进入 fulfilledrejected 状态, 不可再改变其状态
  • 一旦 promise 改变了其状态, 它笔芯有一个值(这个值也可能是 undefined)

开始实现一个 Promise

首先, 让我们看看一段最普通的异步代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 异步方法定义
var basicAsyncFunc = function(callback) {
  setTimeout(function() {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) {
      callback(null, randomNumber)
    } else {
      callback(new Error('bad luck...'))
    }
  }, 1000)
}

// 异步方法调用
basicAsyncFunc((err, result) => {
  if (err) {
    console.log(`the reason fail is: ${err}`)
    return
  }
  console.log(`success get result: ${result}`)
})

按照 Promise 的规范定义, 理想中 Promise 的调用方式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {}

// Promise 形式的异步方法调用
promiseAsyncFunc.then(
  data => {
    console.log(`success get result: ${data}`)
  },//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
  err => {
    console.log(`the reason fail is: ${err}`)
  }
)

按照这个理想当中的调用方式, 让我们写出第一版代码.

第一版 Promise:能保存回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfillCallback(randomNumber)
    else rejectCallback(randomNumber)
  }, 1000)
  return {
    then: function(_fulfillCallback, _rejectCallback) {
      fulfillCallback = _fulfillCallback
      rejectCallback = _rejectCallback
    }
  }
}

// Promise 形式的异步方法调用
promiseAsyncFunc().then(fulfillCallback, rejectCallback)

我们的思路是在 .then() 方法中, 将 fullfillreject 结果的回调函数保存下来, 然后在异步方法中调用. 因为是异步调用, 根据 event-loop 的原理, promiseAsyncFunc().then(fulfillCallback, rejectCallback) 传入的 callback 在异步调用结束时一定是已经赋值过了.

第二版 Promise:实构造函数

当前我们的实现 Promise 中,异步逻辑代码和 Promise 的代码是杂糅在一起的,让我们将其区分开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (fulfillCallback && typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject: function(err) {
      if (rejectCallback && typeof rejectCallback === 'function') {
        rejectCallback(err)
      }
    },
    then: function(_fulfillCallback, _rejectCallback) {
      fulfillCallback = _fulfillCallback
      rejectCallback = _rejectCallback
    }
  }
}

let ownPromise = function(asyncCall) {
  let promise = promiseAsyncFunc()
  asyncCall(promise.fulfill, promise.reject)
  return promise
}

// Promise 形式的异步方法调用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})

我们新定义了一个方法 ownPromise() 用于创建 Promise,并在promiseAsyncFunc() 中暴露出 fulfillreject 接口方便异步代码去调用。

这里有一个问题,我们在调用 ownPromise()后得到了 promise 实例,此时我们可以直接调用 fulfill(),reject()这两个方法,而理论上我们应该只应暴露 promise 的then()方法。所以我们利用闭包将这两个方法隐藏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (fulfillCallback && typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
    reject: function(err) {
      if (rejectCallback && typeof rejectCallback === 'function') {
        rejectCallback(err)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

// Promise 形式的异步方法调用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})

第三版 Promise: 支持状态管理

为了实现规范中对于 Promise 状态变化的要求, 我们需要为 Promise 加入状态管理, 这一步较为简单, 让我们看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (status !== PENDING) return
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
        status = FULFILLED
      }
    },//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
    reject(error) {
      if (status !== PENDING) return
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
        status = REJECTED
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

// Promise 形式的异步方法调用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
}).then(data => console.log(data), err => console.log(err))

这段代码中我们用到了 Symbol 来表示状态常量, 对 Symbol 不了解的同学可以看这里

为了判断 Promise 的状态, 我们加入了 fulfillreject 两个方法。并在其中判断 promise 当前状态。如果不是 pending 状态则直接 return(因为 Promise 状态只可能改变一次)。

现在我们的 promise 实现了对状态控制的规范:

  • 只允许改变一次状态
  • 只能从 pending => fulfilled 或 pending => rejected

但是我们的 Promise 有一个问题: promise 的值没有被保存下来。如果 promise 在异步调用完成之后才被调用 .then() 方法,则我们无法把异步调用的结果传递给回调函数。为此我们需要为 Promise 加一个 value 字段:

第四版 Promise: 保存异步调用的结果

我们为 promise 加入 value 字段,用于保存 Promise 的执行结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = _value
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = error
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

这里我们又发现一个问题,如果一个 Promise 已经是fulfill或reject状态。我们再调用 then() 方法时,传入的回调方法永远不会被调用(因为 status 已经不是 pending)。

所以我们需要在 then()方法中对其状态进行判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = _value
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = error
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
      }
    },//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        if (status === REJECTED) {
          _rejectCallback(value)
          return
        }
        if (status === FULFILLED) {
          _fulfillCallback(value)
          return
        }
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

第五版 Promise: 支持链式调用

为了支持链式调用,.then() 方法的返回值必须是用 thenable (根据 Promise/A+ 规范, .then() 方法的返回值需要是一个新的 Promise)

为此我们加入一个工具方法 makeThenable()。如果传入的 value 本身就有 then()方法,则直接返回 value。否则返回一个有 then()方法的对象。 在该对象的 then()方法中,我们根据 promise 的状态,调用不同的回调方法生成新的 value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function makeThenable(value, status){
  if(value && typeof value.then === 'function'){
    return value
  }
  if(status === FULFILLED){
    return {
      then: function(fulfillCallback, rejectCallback){
        return makeThenable(fulfillCallback(value), FULFILLED)
      }
    }
  }
  if(status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback){
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

有了以上的 makeThenable()方法,我们可以在 promise 的fulfill(),reject()回将 value 设置为 thenable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED) // 保证当前promise的value为 thenable
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED) 、、  // 保证当前value为 thenable
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
    },
    promise: {
      then: function(){}
    }
  }
}

接下来让我们看 then()方法。为了返回一个新的 promise,我们首先得创建一个新的 promise。其次当前 promise 在fulfill() 或 reject()时,应该调用新的 promise 的fullfill() 或 reject()方法。所以我们在将 fulfullCallback和rejectCallback赋值给当前 promise 时,将其包装一下。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
promise: {
  then: function(_fulfillCallback, _rejectCallback) {
    let newPromiseAsyncFunc = promiseAsyncFunc()
    let fulfillFunc = function(value) {
      newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
    }
    let rejectFunc = function(err) {
      newPromiseAsyncFunc.fulfill(_rejectCallback(err))
    }
    if (status === PENDING) {
      fulfillCallback = fulfillFunc
      rejectCallback = rejectFunc
    } else {
      value.then(fulfillFunc, rejectFunc)
    }
    return newPromiseAsyncFunc.promise
  }//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
}

如此,我们变得到了一个可以链式调用的 promise。让我们来测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(fulfillCallback(value), FULFILLED)
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED)
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED)
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        let newPromiseAsyncFunc = promiseAsyncFunc()
        let fulfillFunc = function(value) {
          newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
        }
        let rejectFunc = function(err) {
          newPromiseAsyncFunc.fulfill(_rejectCallback(err))
        }
        if (status === PENDING) {
          fulfillCallback = fulfillFunc
          rejectCallback = rejectFunc
        } else {
          value.then(fulfillFunc, rejectFunc)
        }
        return newPromiseAsyncFunc.promise
      }
    }
  }
}//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

let testChainedPromise = ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})
  .then(
    data => {
      console.log(data)
      return 'return value in then1 fulfill'
    },
    err => {
      console.log(err)
      return 'return value in then1 reject'
    }
  )
  .then(
    data => {
      console.log(data)
      return 'return value in then2 fulfill'
    },
    err => {
      console.log(err)
      return 'return value in then2 reject'
    }
  )
  .then(
    data => {
      console.log(data)
    },
    err => {
      console.log(err)
    }
  )

/**
console output:

0.9931984611850693
return value in then1 fulfill
return value in then2 fulfill
*/

第六版 Promise: Error handling

这里我们只对异步调用fulfill 回调中抛出的 error 进行处理。

首先是异步调用部分,我们将其 try catch 起来,在发生异常时调用 reject 方法,并将异常作为参数传入。

1
2
3
4
5
6
7
8
9
10
let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  try {
    asyncCall(defer.fulfill, defer.reject)
  } catch (e) {
    defer.reject(e)
  }//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
  return defer.promise
}

然后是 fulfill 中可能出现的异常。我们对fulfillCallback(value)可能出现的异常进行捕获,并将异常传递给rejectCallback。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        try {
          let newValue = fulfillCallback(value)
          return makeThenable(newValue, FULFILLED)
        } catch (e) {
          return makeThenable(rejectCallback(e), FULFILLED)
        }
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

最后让我们对完整的代码进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        try {
          let newValue = fulfillCallback(value)
          return makeThenable(newValue, FULFILLED)
        } catch (e) {
          return makeThenable(rejectCallback(e), FULFILLED)
        }
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

// Promise 形式的异步方法定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED)
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED)
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
      status = REJECTED
    },//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        let newPromiseAsyncFunc = promiseAsyncFunc()
        let fulfillFunc = function(value) {
          newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
        }
        let rejectFunc = function(err) {
          newPromiseAsyncFunc.fulfill(_rejectCallback(err))
        }
        if (status === PENDING) {
          fulfillCallback = fulfillFunc
          rejectCallback = rejectFunc
        } else {
          value.then(fulfillFunc, rejectFunc)
        }
        return newPromiseAsyncFunc.promise
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  try {
    asyncCall(defer.fulfill, defer.reject)
  } catch (e) {
    defer.reject(e)
  }
  return defer.promise
}

let testChainedPromise = ownPromise(function(fulfill, reject) {
  throw Error('here is an error in asyncCall')
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})
  .then(
    data => {
      console.log(data)
      return 'return value in then1 fulfill'
    },
    err => {
      console.log(err.message)
      return 'return value in then1 reject'
    }
  )
  .then(
    data => {
      console.log(data)
      throw Error('here is an error in fulfill1')
      return 'return value in then2 fulfill'
    },
    err => {
      console.log(err.message)
      return 'return value in then2 reject'
    }
  )
  .then(
    data => {
      console.log(data)
    },
    err => {
      console.log(err.message)
    }
  )//IT平头哥联盟∙苏南的专栏 交流:912594095、公众号:honeyBadger8


// console out:
Error: here is an error in asyncCall
return value in then1 reject
Error: here is an error in fulfill1
return value in then2 reject

总结

以上就是我们对于 Promise 的一个简单的实现,实现思路主要参考了 Q-A promise library for javascript。该实现的 Promise 功能较为简陋,仅实现了部分 api/规范。有任何意见和建议欢迎在评论区交流 ;)

进一步阅读 && 引用

对于 Promise 使用以及error handle 的讲解:

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261

Promise 实现库之一: Q 对于 Promise 实现的讲解:

https://github.com/kriskowal/q/blob/master/design/README.md

热门推荐


@IT·平头哥联盟-首席填坑官·苏南
扫码关注公众号,获取更多学习资源

(转载本站文章请注明作者和出处 首席填坑官-苏南的博客

本文为[转载]文章,转载请标明原文出处。
原文链接:https://my.oschina.net/u/3413999/blog/2988091
本文链接:https://www.susouth.com/reprint/2019/01/08/promise/
阿里云1023 国内首家采用 amd EPYC™霄龙处理器的实例,业界领先的性价比,0.57元/天起
宝剑锋从磨砺出,梅花香自苦寒来,用心分享,一起成长,做有温度的攻城狮!