Promise 解决异步回调多么清爽,手写简单promise,打开异步世界的大门

198 阅读4分钟

在 JavaScript 的异步编程领域的一个强大的工具:promise。能够有效的管理各种异步操作,让原本混乱的代码变的井井有条。不管是在工作中,还是在面试的时候,promise也是一个常被用到和被提问到的点,那么让我们进行一个探究,写一个简单版的promise,了解其中的逻辑。

image.png

了解promise

作用:

  • 用来解决回调地狱的
  • 利用的是then方法的链式调用

promise实例的特点:

  1. 三种状态(pending等待状态、Fulfilled已完成状态、Rejected已拒绝状态)
  2. 三个方法(thencatchfinally)还有一些静态方法(all,allsettled等等)

promise对象两个属性

  1. promiseResult(表示异步返回的值)
  2. promiseState(当前promise对象的状态,默认是pending)

promise回调函数接收两个参数

  1. resolve
  2. reject

日常开发实际情况:

  1. async + await + promise搭配使用
  2. promise不是最佳的解决回调地狱的方案,因为其本身在使用回调
  3. async + await可以摆脱回调,最终能做到的将异步的任务转换成同步的流程

promise解决回调地狱问题的小案例

function top1(callback) {
    setTimeout(() => {
        callback('数据1');
    }, 1000);
}


function top2(data1, callback) {
    setTimeout(() => {
        callback(data1 + '数据2');
    }, 1000);
}

function top3(data2, callback) {
    setTimeout(() => {
        callback(data2 + '数据3');
    }, 1000);
}

top1((data1) => {
    top2(data1, (data2) => {
        top3(data2, (finalData) => {
            console.log(finalData);
        });
    });
});

image.png image.png

对于上面这个代码不知道大家有没有跟我一样,第一次看,单个代码很好理解,但是下面这个是个什么逻辑,不太好理解。对于这种情况,下面有对这个代码进行一个解析。但是同时我们也看出其复杂之处。 使用promise进行修改后可以说结构化非常的清晰。希望通过这样的一个例子可以加深对它的理解。

function top1() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('数据1');
        }, 1000);
    });
}

function top2(data1) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(data1 + '数据2');
        }, 1000);
    });
}

function top3(data2) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(data2 + '数据3');
        }, 1000);
    });
}

top1()
  .then((data1) => top2(data1))
  .then((data2) => top3(data2))
  .then((finalData) => console.log(finalData));

手写一个简单的promise

弄清楚上面的代码,了解到函数变量的知识,我们可以去研究promise的手写代码【此代码比较简单,不完善,旨在理解promise的相关过程】

function Promise(callback) {
        this.promiseResult = undefined;
        this.promiseState = "pending";
        const _this = this;
 
        //resolve函数
        function resolve(value) {
          if (_this.promiseState === "pending") {
            _this.promiseState = "fulfilled";
            _this.promiseResult = value;
          }
        }
 
        //reject函数
        function reject(error) {
          if (_this.promiseState === "pending") {
            _this.promiseState = "rejected";
            _this.promiseResult = error;
          }
        }
 
        //回调函数
        callback(resolve, reject);
      }
 
 const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(1);
        }, 2000);
      });

相比起上面的案例,这段代码应该是比较清晰的

1.首先根据promise的返回值,我们在封装时候,先将promiseResult = undefined;promiseState = "pending";建立一个初始的状态。

2.封装了两个resolvereject函数(用于修改状态和赋值)

  1. callback() 函数就是 (resolve, reject) => { setTimeout(() => { resolve(1); }, 2000); } 此时的resolve和reject只是变量。内容是promise内部的封装的

  2. 最后callback(resolve, reject)输出结果,此时的resolvereject是两个函数传递给了callback里面,而setimeout里面调用了resolve(1)函数执行了我们在promise里面封装的resolve函数

案例代码解析

函数变量

我们可以先了解 函数变量 的使用,方便后续理解。

函数变量:顾名思义就是把一个函数当作变量

简单的使用:函数b作为变量传给了 a函数,并在a中作为callback()触发

function a(num, callback) {
  console.log("there is a number : " + num + "!");
  callback();
}
 
function b() {
  console.log("yes!");
}

// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, b);

另外的写法:初学时可能被另外写法混淆【需要注意:当内容多了容易混淆】

function a(num, callback) {
  console.log("there is a number : " + num + "!");
  callback();
}
 
// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, function b() {
  console.log("yes!");
});

// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, ()=>{
  console.log("yes!");
});

代码分解

根据我们的前提,()=>{}这样的一个结构就是传了一个函数,然后我们将这个代码进行分解

top1(

(data1) => {
    top2(data1, (data2) => {
        top3(data2, (finalData) => {
            console.log(finalData);
        });
    });
}  
// 这整个就是传给top1的 callback函数
//而top1函数里面的 callback('数据1')的'数据1'就是data1

);

==========将它换一种写法(代入进去)============
top1(callback){ //这里我还是用callback代替方便查看
  setTimeout(() => {
 //------------------------------------
  ('数据1') => {
    top2(data1, (data2) => {
        top3(data2, (finalData) => {
            console.log(finalData);
        });
    });
 }
//-------------------------------------
    }, 1000);
}

根据上面这个代码的分解,依次类推,我们可以把里面的 ()=>这种形式进行替代,方便我们的理解查看【以下代码写法是有问题的,只是方便理解这个过程】

top1(callback){
    // top1
    setTimeout(() => {
        // top1的callback('数据1')
        ('数据1') => {
            top2(data1, callback){
                // top2
                setTimeout(() => {
                    // top2的callback(data1 + '数据2')
                    (data1 + '数据2') => {
                        top3(data2, callback){
                            // top3
                            setTimeout(() => {
                                // top3的callback(data2 + '数据3')
                                (data2 + '数据3') => {
                                    console.log(data2 + '数据3');
                                }
                            }, 1000);

                        };
                    }

                }, 1000);

            };

        }
    }, 1000);
}