当前位置:Gxlcms > JavaScript > promise对象的深入解析(附示例)

promise对象的深入解析(附示例)

时间:2021-07-01 10:21:17 帮助过:7人阅读

本篇文章给大家带来的内容是关于promise对象的深入解析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

js中的异步,刚开始的时候都是用回调函数实现的,所以如果异步嵌套的话,就有出现回调地狱,使得代码难以阅读和难以维护,后来es6出现了promise,解决了回调地狱的问题。现在我们就自己写代码实现一下promise,这样才能深入理解promise的运行机制,对以后使用promise也能够更加得心应手。开始之前可以先看下promise的官网promise/A+

先来看下promise的用法

  1. new Promise((resolve,reject)=>{
  2. resolve(1);
  3. reject(11);
  4. }).then(res=>{
  5. console.log(res);
  6. setTimeout(()=>{
  7. return new Promise((resolve,reject)=>{
  8. resolve(2)
  9. })
  10. },1000)
  11. }).then(res2=>{
  12. console.log(res2);
  13. });

控制台打印
1
...1s later
2

先分析下上面这段代码,先提出几个问题
1.第一段resolve和reject都有,但是只输出了1,为什么?
2.then里的res是如何取到resolve中的值的?
3.promise是如何做到链式调用的?

状态机

promise中有个状态机的概念,先说下为什么要有状态机的概念呢,因为promise的状态是单向变化的,有三种状态,pending,fullfilled,rejected,而这三种状态只能从pending->fullfilled或者pending->rejected这两种形式,也就是说执行了fullfilled之后,就不会执行rejected。这就解释了上面的第一个问题。

下面我们来看下具体实现的完整代码

  1. const PENDING = 'PENDING';
  2. const FULLFILLED = 'FULLFILLED';
  3. const REJECTED = 'REJECTED';
  4. class Promise{
  5. constructor(fn){
  6. this.status = PENDING;//状态
  7. this.data = undefined;//返回值
  8. this.defercb = [];//回调函数数组
  9. //执行promise的参数函数,并把resolve和reject的this绑定到promise的this
  10. fn(this.resolve.bind(this),this.reject.bind(this));
  11. }
  12. resolve(value){
  13. if(this.status === PENDING){
  14. //只能pending=>fullfied
  15. this.status = FULLFILLED;
  16. this.data = value;
  17. this.defercb.map(item=>item.onFullFilled());
  18. }
  19. }
  20. reject(value){
  21. if(this.status === PENDING){
  22. //只能pending=>rejected
  23. this.status = REJECTED;
  24. this.data = value;
  25. this.defercb.map(item=>item.onRejected());
  26. }
  27. }
  28. then(resolveThen,rejectThen){
  29. //如果没有resolveThen方法,保证值可以穿透到下一个then里有resolveThen的方法中
  30. resolveThen = typeof resolveThen === 'function' ? resolveThen : function(v) {return v};
  31. rejectThen = typeof rejectThen === 'function' ? rejectThen : function(r) {return r};
  32. //返回的都是promise对象,这样就可以保证链式调用了
  33. switch(this.status){
  34. case PENDING:
  35. return new Promise((resolve,reject)=>{
  36. const onFullFilled = () => {
  37. const result = resolveThen(this.data);//这里调用外部then的resolveThen方法,将值传回去
  38. //如果返回值是promise对象,执行then方法,取它的结果作为新的promise实例的结果,因为this.data会重新赋值
  39. result instanceof Promise && result.then(resolve,reject);
  40. }
  41. const onRejected = ()=>{
  42. const result = rejectThen(this.data);
  43. result instanceof Promise && result.then(resolve,reject);
  44. }
  45. this.defercb.push({onFullFilled,onRejected});
  46. });
  47. break;
  48. case FULLFILLED:
  49. return new Promise((resolve,reject)=>{
  50. const result = resolveThen(this.data);
  51. result instanceof Promise && result.then(resolve,reject);
  52. resolve(result);
  53. })
  54. break;
  55. case REJECTED:
  56. return new Promise((resolve,reject)=>{
  57. const result = rejectThen(this.data);
  58. result instanceof Promise && result.then(resolve,reject);
  59. reject(result)
  60. })
  61. break;
  62. }
  63. }
  64. }

运行下面的例子

  1. new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve(1);
  4. }, 1000);
  5. }).then((res2) => {
  6. console.log(res2);
  7. return new Promise((resolve, reject) => {
  8. setTimeout(() => {
  9. resolve(2);
  10. }, 1000);
  11. });
  12. }).then((res3) => {
  13. console.log(res3);
  14. return new Promise((resolve, reject) => {
  15. setTimeout(() => {
  16. resolve(3);
  17. }, 1000);
  18. });
  19. }).then((res4) => {
  20. console.log(res4);
  21. });

控制台打印
...1s later
1
...1s later
2
...1s later
3
说明上面的实现是没有问题的
不过还有一个问题,就是事件循环的顺序问题,比如执行下面的代码

  1. new Promise((resolve) => {
  2. resolve();
  3. })
  4. .then(() => {
  5. console.log('1');
  6. })
  7. .then(() => {
  8. console.log('2');
  9. });
  10. console.log('3');

并没有像预想中输出3,1,2,而是输出了1,2,3,原因就是因为我们的这个Promise是在主线程中,没有在下一个任务队列中,可以加上settimeout解决这个问题,不过这也只是为了让我们更好理解执行顺序而已,然而实际上是promise是属于微任务中的,而settimeout是属于宏任务,还是不太一样的

以上就是promise对象的深入解析(附示例)的详细内容,更多请关注Gxl网其它相关文章!

人气教程排行