JS | Promise对象的使用详解

目录

Promise是什么?

Promise的理解

抽象表达

具体表达

Promise对象的状态改变

Promise对象结果值属性

Promise的基本使用

Promise的基本流程

为什么要使用Promise?

① 指定回调函数的方式更加灵活

② 支持链式调用,可以解决回调地狱问题

- 什么是回调地狱?

- 回调地狱的缺点

- 回调地狱解决方案

如何使用Promise?

PromiseAPI

- Promise构造函数

- Promise.prototype.then方法

- Promise.prototype.catch方法

- Promise.resolve方法

- Promise.reject方法

- Promise.all方法

- Promise.allSettled方法

- Promise.race方法

Promise的几个关键问题

① 如何改变Promise的状态?

② 一个Promise指定多个成功/失败的回调函数,都会调用吗?

③ 改变Promise状态和指定回调函数谁先执行谁后执行?

④ promise.then()返回新promise的结果状态由什么决定?

⑤ promise如何串连多个操作任务?

⑥ promise异常穿透

⑦ 如何中断promise链?

▶ 参考资料▼


Promise是什么?

Promise的理解

— — 阮大佬的解释— —

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法处理。

抽象表达

1、Promise是一门新的技术(ES6规范)

2、Promise是JS中进行异步编程的新解决方案

备注:旧的方案是单纯使用回调函数

具体表达

1、从语法上来说:Promise是一个构造函数

2、从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功或失败的结果值。

Promise对象的状态改变

Promise的状态指的是实例对象中的一个属性PromiseState,它的值有三个:

pending:未决定的

resolve/fulfilled:成功

rejected:失败

Promise的状态改变只有下面的三种情况:

① 由pending变为resolved

② 由pending变为rejected

③ 抛出异常:如果当前为pending就会变为rejected

说明:只有这2种,且一个Promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason

Promise对象结果值属性

Promise实例对象中还有一个属性PromiseResult,保存着对象成功/失败的结果。要想改变它的值,只有通过给resolve()或者reject()方法传参,成功时,将对应数据传入resolve()方法,失败时,将对应的数据传入reject()方法,之后可以在then(value => {}, reason => {})方法中取出,value对应着resolve()接收的参数,reason对应着reject()接收的参数。

Promise的基本使用

需求:点击按钮,1秒后显示是否中奖(30%概率中奖)。若中奖,弹出:恭喜恭喜,奖品为10万RMB玛莎拉蒂优惠券;若未中奖弹出:再接再厉。

html页面,放个标题和按钮即可:

<h2>Promise 初体验</h2>
<button>点击抽奖</button>

不使用Promise的js代码:

<script>
  // 生成随机数
  function rand(m,n) {
    return Math.ceil(Math.random() * (n - m + 1) + m - 1);
  }
  /** 需求:
   * 点击按钮, 2s 后显示是否中奖(30%概率中奖)
   * 若中奖,弹出:恭喜恭喜,奖品为 10万 RMB 玛莎拉蒂优惠券
   * 若未中奖弹出: 再接再厉 
   */
  // 旧的方法
  // 获取元素对象
  const btn = document.querySelector('button');
  // 绑定单击事件
  btn.addEventListener('click', function () {
    // 定时器
    setTimeout(() => {
      // 30% 中奖概率,如果数字为1—100,那么我们随机数小于30,就认为它中奖了(简易的算法)
      // 获取从 1 - 100 的一个随机数
      let n = rand(1, 100);
      // 判断
      if (n <= 30) {
        alert('恭喜恭喜,奖品为 10万 RMB 玛莎拉蒂优惠券');
      } else {
        alert('再接再厉');
      }
    }, 1000);
 })
</script>

使用Promise,js代码:

<script>
     // 生成随机数
  function rand(m, n) {
     return Math.ceil(Math.random() * (n - m + 1) + m - 1);
 }
  // 获取元素对象
  const btn = document.querySelector('button');
  // 绑定单击事件
  btn.addEventListener('click', function () {
    // Promise 形式实现
    // resovle 解决  是函数类型的数据 异步任务成功时调用
    // reject  拒绝  是函数类型的数据 异步任务失败时调用
    let p = new Promise((resolve, reject) => {
    setTimeout(() => {
      // 30% 中奖概率,如果数字为1—100,那么我们随机数小于30,就认为它中奖了
      // 获取从 1 - 100 的一个随机数
      let n = rand(1, 100);
      // 判断
      if (n <= 30) {
        resolve(); // 将 promise 对象的状态设置为 成功
      } else {
        reject();  // 将 promise 对象的状态设置为 失败
      }
    }, 1000);
     });

     // 调用 then 方法
     // then方法的第一个回调函数,promise对象状态为成功时调用,第二个回调函数,promise对象状态为失败时调用
     p.then(() => {
    alert('恭喜恭喜,奖品为 10万 RMB 玛莎拉蒂优惠券');
     }, () => {
    alert('再接再厉');
     })
 })
</script>

现在需求变更,在弹出提示时,同时告知本次抽到的数字。

不使用Promise的js代码:

<script>
     // 生成随机数
  function rand(m, n) {
     return Math.ceil(Math.random() * (n - m + 1) + m - 1);
 }
  // 旧的方法
  // 获取元素对象
  const btn = document.querySelector('button');
  // 绑定单击事件
  btn.addEventListener('click', function () {
    // 定时器
    setTimeout(() => {
      // 30% 中奖概率,如果数字为1—100,那么我们随机数小于30,就认为它中奖了
      // 获取从 1 - 100 的一个随机数
      let n = rand(1, 100);
      // 判断
      if (n <= 30) {
        alert('恭喜恭喜,奖品为 10万 RMB 玛莎拉蒂优惠券,您的中奖数字为:' + n);
      } else {
        alert('再接再厉,您抽中的数字为:' + n);
      }
    }, 1000);
 })
</script>

使用Promise的js代码:

<script>
     // 生成随机数
  function rand(m, n) {
     return Math.ceil(Math.random() * (n - m + 1) + m - 1);
 }

  // 获取元素对象
  const btn = document.querySelector('button');
  // 绑定单击事件
  btn.addEventListener('click', function () {

    // 如果现在需要在弹出提示时,同时告知本次的号码,该怎么办?
    // 只需要将成功或失败的值分别传入 resolve() 和 reject() 方法,然后再then()方法的两个回调函数中接收即可。
    let p = new Promise((resolve, reject) => {
    setTimeout(() => {
      // 30% 中奖概率,如果数字为1—100,那么我们随机数小于30,就认为它中奖了
      // 获取从 1 - 100 的一个随机数
      let n = rand(1, 100);
      // 判断
      if (n <= 30) {
        resolve(n); // 将 promise 对象的状态设置为 成功
      } else {
        reject(n);  // 将 promise 对象的状态设置为 失败
      }
    }, 1000);
     });

     // 调用 then 方法
     // then方法的第一个回调函数,promise对象状态为成功时调用,第二个回调函数,promise对象状态为失败时调用
     // value 值
     // reason 理由
     p.then((value) => {
    alert('恭喜恭喜,奖品为 10万 RMB 玛莎拉蒂优惠券,您的中奖数字为:' + value);
     }, (reason) => {
    alert('再接再厉, 您的号码为:' + reason);
     })
 })
</script>

Promise的基本流程

为什么要使用Promise?

① 指定回调函数的方式更加灵活

旧的:必须在启动异步任务前指定

promise:启动异步任务=> 返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定一个或多个)

② 支持链式调用,可以解决回调地狱问题

- 什么是回调地狱?

回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

例如,下面的代码是Node.jsfs模块读取三个文件,并将读取到的结果进行拼接并且输出到控制台中。

fs.readFile('./resource/1.txt', (err, data1) => {
  if (err) throw err;
  fs.readFile('./resource/content.txt', (err, data2) => {
    if (err) throw err;
    fs.readFile('./resource/2.txt', (err, data3) => {
      if (err) throw err;
      console.log(data1 + data2 + data3);
    })
  })
})

这个代码片段,fs.readFile()在嵌套调用,这里就形成了回调地狱。

- 回调地狱的缺点

① 不便于阅读

② 不便于异常处理

- 回调地狱解决方案

① 使用promise链式调用

参考:ES6新增对象 - Promise使用方法详解_创建promise对象-优快云博客

② 终极解决方案:async / await

参考:JS | ES中的异步方法 async /await 详解

如何使用Promise?

PromiseAPI

- Promise构造函数

Promise的构造函数:Promise(executor){}

① executor函数:执行器(resolve, reject) => {}

② resolve函数:内部定义成功时我们调用的函数

③ reject函数:内部定义失败时我们调用的函数

说明:executor会在Promise内部立即同步调用,异步操作在执行器中执行。

- Promise.prototype.then方法

① Promise.prototype.then方法:(onResolved, onRejected) => {}

② onResolved函数:成功的回调函数,通常这么写: value => {}

③ onRejected函数失败的回调函数,通常这么写reason => {}

说明:指定用于得到成功value的成功回调和用于得到reason的失败回调,返回一个新的Promise对象。

- Promise.prototype.catch方法

Promise.prototype.catch方法:(onRejected) => {}

onRejected函数:失败的回调函数,通常这么写reason => {}

catch()只能指定失败的回调,不能指定成功的回调。其实也是使用then()实现的.

说明:catch() then()的语法糖, 相当于:then(undefined, onRejected) 。

- Promise.resolve方法

Promise.resolve方法:(value) => {}

value成功的数据或Promise对象

说明:返回一个成功/失败Promise对象

 catch()方法和resolve()方法的使用:

<script>
   // 如果传入的参数为 非Promise类型的对象,返回的结果为 成功的Promise对象
   // 如果传入的参数为 Promise对象,则参数的结果决定了resolve的结果
   let p1 = Promise.resolve(555);
   console.log(p1); // Promise {<fulfilled>: 555}
 ​
   let p2 = Promise.resolve(new Promise((resolve, reject) => {
      resolve('OK');
   }));
 ​
   console.log(p2); // Promise {<fulfilled>: "OK"}
 ​
   // 传入失败的Promise
   let p3 = Promise.resolve(new Promise((resolve, reject) => {
     reject('Error');
   }));
   console.log(p3);    // Promise {<rejected>: "Error"}
   // 失败的同时还报错了,原因是没有对错误进行处理,这里使用catch()进行处理就能解决报错
   p3.catch(reason => {
     console.log(reason);    // Error 
   })
</script>

注:关于resolve()方法参数问题

① 如果传入的参数为 非Promise类型的对象,返回的结果为 成功的 Promise 对象;

② 如果传入的参数为 Promise对象,则参数的结果决定了 resolve 的结果

- Promise.reject方法

Promise.reject方法:(reason) => {}

reason失败的原因

说明:返回一个失败的Promise对象

 reject()方法的示例:

<script>
  // 总结,Promise.reject()方法,无论你传入什么得到的都是失败的Promise对象,并且失败的结果就是你传入的参数。
  let p = Promise.reject(555);
  console.log(p);
  let p2 = Promise.reject('hello');
  console.log(p2);
  let p3 = Promise.reject(new Promise((resolve, reject) => {
     resolve('OK');
  }))    
  console.log(p3);
</script>

注:Promise.reject()方法,无论你传入什么得到的都是失败的Promise对象,并且失败的结果就是你传入的参数。 

- Promise.all方法

Promise.all方法:(promises) => {}

promises参数:包含nPromise对象的数组

说明:返回一个新的Promise对象,只有所有的Promise都成功才成功,只要有一个失败了就直接失败,失败的结果就是失败的Promise对象的结果。

 all()方法的示例:

 <script>
     let p1 = new Promise((resolve, reject) => {
         resolve('OK');
     });
         
     let p2 = Promise.resolve('Success');
     let p3 = Promise.resolve('Oh Yeah');
 ​
     let result1 = Promise.all([p1, p2, p3]);
 ​
     console.log(result1); // 返回一个成功的Promise对象,对象的结果是一个数组,数组中存有三个成功的promise对象的结果
 ​
     // 如果数组中 p2 是一个失败的Promise,那么all()方法返回的是一个失败的Promise对象,并且失败的结果是数组中失败的Promise对象的结果。
     p2 = Promise.reject('Error');
     let result2 = Promise.all([p1, p2, p3]);
     console.log(result2);
 </script>
- Promise.allSettled方法

Promise.allSettled方法:(promises) => {}

promises参数:包含nPromise对象的数组

说明:该方法返回的结果总是状态为成功的Promise对象,这个Promise对象的结果值是包含promises参数中每一个promise对象的状态以及结果值的对象数组。注意和all()方法的区别。

​ allSettled()方法示例:

<script>
  // 声明两个Promise对象
  let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('商品数据 - 1');
      // reject('ERROR')
    }, 1000);
  })
  let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      // resolve('商品数据 - 2');
      reject('出错了');
    }, 1000);
  })

  // 调用allSettled() 方法  该方法返回的结果总是成功的Promise对象,
  // 它的结果值是包含每一个promise对象的状态以及结果值的对象。
  let result = Promise.allSettled([p1, p2]);
  console.log(result);

  let res = Promise.all([p1, p2]);    // 全部成功,才会返回成功的Promise对象
  console.log(res);
 ​
</script>

在开发者工具中,执行代码,结果如下: 

- Promise.race方法

Promise.race方法:(promises) => {}

promises参数:包含nPromise对象的数组

说明:返回一个新的Promise对象,第一个完成的Promise对象的结果状态就是最终的结果状态

 race()方法的示例:

 <script>
     // 同步代码
     let p1 = new Promise((resolve, reject) => {
         resolve('OK');
     })    
     let p2 = Promise.resolve('Success');
     let p3 = Promise.resolve('Oh Yeah');
     // 调用
     let result1 = Promise.race([p1, p2, p3]);
     // 代码从上往下执行,所以p1最先执行,所以result的状态和结果应该是p1的状态和结果。
     console.log(result1);    
 ​
     // 异步代码
     let p4 = new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve('OK');
         }, 2000);
     })
     let p5 = Promise.reject('Error');
     let p6 = Promise.resolve('Oh Yeah');
     let result2 = Promise.race([p4, p5, p6]);
     // p4为异步代码,p5 会先于p4、p6执行,所以p5的状态和结果即为result2的状态和结果。
     console.log(result2);
 </script>

 在开发者工具中,执行代码,结果如下: 

Promise的几个关键问题

① 如何改变Promise的状态?

改变Promise的状态有三种方法:

resolve(value):如果当前是pending就会改变为resolved(fulfilled)

reject(reason):如果当前是pending就会变为rejected

抛出异常:如果当前是pending就会变为rejected

示例:

<script>
  let p = new Promise((resolve, reject) => {
    // 1. resolve函数
    // resolve('OK');   // pending  => fulfilled(resolved)
    // 2. reject 函数
    // reject('Error');    // pending  => rejected
    // 3. 抛出错误
    // throw '出问题了';    // pending  => rejected
  })
  console.log(p); 
</script>

② 一个Promise指定多个成功/失败的回调函数,都会调用吗?

指定回调用的就是then(),所以,这个题目的意思是如果使用then()方法为一个promise对象指定多个回调,那么这些回调是否都会执行?

答案是:当Promise改变为对应状态时都会调用。

示例:

<script>
     let p = new Promise((resolve, reject) => {
         resolve('OK');
     })   
 ​
     // 指定回调 - 1
     p.then(value => {
         console.log(value);
     })
 ​
     // 指定回调 - 2
     p.then(value => {
         alert(value);
     })
     // 有弹框,控制台有输出,说明这两个回调都执行了。
 </script>

在开发者工具中,执行代码,演示结果如下:  

③ 改变Promise状态和指定回调函数谁先执行谁后执行?

1、都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调

2、如何先改状态再指定回调?

①  在执行器中直接调用resolve()/reject()

then()方法延迟更长时间才被调用

3. 什么时候才能得到数据?

① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据

② 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据

示例:

<script>
let p = new Promise((resolve, reject) => {
    // setTimeout(() => {   //  异步任务
    resolve('OK');  // 同步任务
    // }, 1000)
})    
p.then(value => {
    console.log(value); 
}, reason => {
  })
// 1. 当Promise构造函数中的executor执行器函数内部是同步任务时,先改变状态,在执行then()方法指定回调(注意是指定回调,不是执行回调)​
// 2. 当Promise构造函数中的executor执行器函数内部是异步任务时,那么是先执行then()方法指定回调函数,再改变状态。
</script>

1. 当Promise构造函数中的executor执行器函数内部是同步任务时,先改变状态,在执行then()方法指定回调(注意是指定回调,不是执行回调)​

2. 当Promise构造函数中的executor执行器函数内部是异步任务时,那么是先执行then()方法指定回调函数,再改变状态。 

④ promise.then()返回新promise的结果状态由什么决定?

1、简单表达:由then()指定的回调函数执行的结果决定。

2、详细表达:

  • 如果抛出异常,新promise变为rejectedreason为抛出的异常
  • 如果返回的是非promise的任意值,新promise变为resolvedvalue为返回的值
  • 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果

示例:

<script>
  let p = new Promise((resolve, reject) => {
    resolve("OK");
    // reject('error')
  });

  // 执行 then 方法
  let result = p.then(
    (value) => {
      // p 为成功的回调
      // console.log(value); // result的状态为fulfilled,结果为undefined,因为没有返回值

      // 1. 抛出错误
      // throw '出了问题'; // result 的状态为rejected,结果为'出了问题'

      // 2. 返回结果为非 Promise 类型的对象
      // return 555; // result的状态为fulfilled,结果为555

      // 3. 返回结果是Promise对象
      return new Promise((resolve, reject) => {
        // resolve('success'); // result的状态为fulfilled,结果为success

        // reject('error'); // result 的状态为rejected,结果为error

        throw "出了问题"; // result 的状态为rejected,结果为'出了问题
      });
    },
    (reason) => {
      // p 为失败的回调
      // console.log(reason); // result的状态为fulfilled,结果为undefined,因为没有返回值。 这里本身会输出error

      // 1. 抛出错误
      // throw '出了问题'; // result 的状态为rejected,结果为'出了问题'

      // 2. 返回结果为非 Promise 类型的对象
      // return 555; // result的状态为fulfilled,结果为555

      // 3. 返回结果是Promise对象
      return new Promise((resolve, reject) => {
    // resolve('success'); // result的状态为fulfilled,结果为success
    // reject('error');  // result 的状态为rejected,结果为error
    // throw '出了问题';   // result 的状态为rejected,结果为'出了问题'
  });
    }
  );
  console.log(result);
</script>

⑤ promise如何串连多个操作任务?

1、promisethen()返回一个新的promise,可以继续调用then()方法,形成链式调用

2、通过then的链式调用串连多个同步或异步任务

示例:

<script>
    let p = new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve('OK');
     }, 1000);
    })
    p.then(value => {
       return new Promise((resolve, reject) => {
          resolve('success');
       })
    }).then(value => {
       console.log(value)  // success
    }).then(value => {
       console.log(value)  // undefined
    })
</script>

⑥ promise异常穿透

在调用链的最后指定失败的回调,就可以处理任务当中出现的错误,这个现象称为异常穿透

1、当使用promisethen链式调用时,可以在最后指定失败的回调

2、前面任何操作除了异常,都会传到最后失败的回调中处理

 <script>
     let p = new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve('OK')
             // reject('Err')
         }, 1000);
     })
     p.then(value => {
         // console.log(111);
         throw '出问题了';
     }).then(value => { // 也可以在这里处理上面出现的错误
         console.log(222)  
     }).then(value => {
         console.log(333)  
     }).catch(reason => { // 这里使用catch,也可以使用then
         console.warn(reason);
     })        ​
     // 在调用链的最后指定失败的回调,就可以处理任务当中出现的错误,这个现象称为异常穿透。
 </script>

⑦ 如何中断promise链?

当使用promisethen链式调用时,想要在中间中断,不再调用后面的回调函数,有且只有一个办法:在回调函数中返回一个pending状态的promise对象。

<script>​
     // 如果想要中断这个then调用链,有且只有一个方式,在需要中断的回调函数中,返回一个pending状态的promise对象
     let p = new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve('OK');
         }, 1000);
     })
     p.then(value => {
         console.log(111);
         // 假如后面不想让它调用了,有且仅有一个方式:返回一个pending状态的promise对象
         return new Promise(() => {});
     }).then(value => {  
         console.log(222)  
     }).then(value => {
         console.log(333)  
     }).catch(reason => {    
         console.warn(reason);
     })      
 </script>

▶ 参考资料▼

—— ES6新增对象 - Promise使用方法详解_创建promise对象-优快云博客 ——

—— JS | ES中的异步方法 async /await 详解 - 优快云博客 ——

—— JS | ES6 新增函数类型——Generator 函数使用方法详解-优快云博客 ——

Promise对象是什么?- 博客园Promise对象的含义和基本用法 - 博客园

萌新如何理解Promise对象 - 简书 | Promise—解读Promise对象 - 优快云专栏

### JavaScriptPromise 的用法与示例 #### 什么是 PromisePromise 是一种用于处理异步操作的对象,它表示一个最终会完成或者失败的操作的结果。通过使用 Promise,可以更清晰地管理复杂的异步流程。 #### 基本结构 Promise 对象有三种状态:`pending`(等待)、`fulfilled`(已成功)和 `rejected`(已失败)。当 Promise 被执行时,其状态会从 `pending` 变为 `fulfilled` 或者 `rejected`[^1]。 以下是基本语法: ```javascript const myPromise = new Promise((resolve, reject) => { // 异步操作逻辑 if (/* 条件 */) { resolve(value); // 成功时调用 } else { reject(error); // 失败时调用 } }); ``` #### 使用 `.then()` 和 `.catch()` `.then()` 方法用于处理成功的回调,而 `.catch()` 则捕获错误并提供相应的处理机制。 ```javascript myPromise .then(result => console.log(`Success: ${result}`)) // 处理成功的情况 .catch(error => console.error(`Error: ${error.message}`)); // 处理失败的情况 ``` #### 链式调用 由于返回的是一个新的 Promise 实例,因此可以通过链式调用来简化多个异步操作的顺序执行。 ```javascript new Promise(resolve => setTimeout(() => resolve(1), 1000)) .then(val => val * 2) .then(val => val + ' is the result') .then(console.log); // 输出:"2 is the result" ``` #### 并行执行多个 Promise 如果需要同时启动多个异步任务,则可以使用 `Promise.all([])` 方法来等待所有的 Promise 完成后再继续下一步。 ```javascript Promise.all([ fetch('/api/data1').then(response => response.json()), fetch('/api/data2').then(response => response.json()) ]) .then(([data1, data2]) => { console.log(data1, data2); }) .catch(err => console.error('An error occurred:', err)); ``` #### 错误处理的最佳实践 为了更好地控制异常传播路径,在设计复杂的应用程序时应始终考虑如何优雅地处理潜在的错误情况。 ```javascript function fetchData(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onload = () => (xhr.status === 200 ? resolve(xhr.responseText) : reject(new Error('Failed'))); xhr.onerror = () => reject(new Error('Network Error')); xhr.send(); }); } fetchData('/some/valid/url') .then(data => JSON.parse(data)) .then(processedData => console.log(processedData)) .catch(e => alert(e.message)); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的烤地瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值