promise相关知识详解

前言

接触promise有一段时间了,不管是自己从书上、看视频教学、看他人的博客记录,感觉都没有很细致的了解promise,promise到底是干嘛的?为什么有resolve、reject还要有then、catch、Promise.resolve等函数?我到底怎么才能用到promise?为什么面试的时候需要我写一个promise? 这些都是我遇到的一些问题,下面是我微薄的见解与记录,希望看完能帮到读者一点点。

promise的初始了解

promise基本概念

promise是ES6提出来的,promise本身是同步的,它可以包裹异步请求,解决了用ajax产生的回调地狱问题,同时async函数返回的也是一个promise对象、await后面如果接的是promise对象则直接返回该值、axios也是基于promise进行封装的。

promise其实就是返回的一个状态标记,里面可以放同步方法,也可以放异步方法,改变状态的是三种,可以理解为后续再去通过回调方法去把成功或失败的值拿到,或者利用。

promise,中文就是承诺,其创建的函数初始状态为pending,改变后有两种状态成功(resolve)、失败(reject),当状态改变后就不允许第二次的状态改变,一般是把 异步操作放在promise中

Promise的状态与值介绍

1、Promise的状态介绍

实列对象中的一个属性:PromiseState

PromiseState:Pending、resolved、rejected

改变前的状态是Pending,只能改变为resolved、rejected,resolved、rejected之间不能互相改变,只能pending向它们改变

2、promise对象中的值

实列中对象的另一个属性:PromiseResult

保存着成功/失败的结果

创建一个promise

   <script>
      var promise = new Promise((resolve, reject) => {
           //异步操作
    
          //成功的结果调用resolve函数
            resoleve()
    
          //失败的结果调用reject函数
           reject()
      });
    </script>
   
    

可以看到Promise可以通过new方法来得到,也就是构造函数,我们看下promise对象中包含什么?

image-20211218132109466

扫描二维码关注公众号,回复: 14859911 查看本文章

可以看出,创建变量名为promise其实本质就是一个promise对象,千万不要觉得这里很无聊,我们只有了解最简单的开始后面才会理解。

promise对象中的内容:

1、PromiseStatus:保存的状态,如果在promise函数内没给状态改变,就为pending

2、PromiseValue:保存的值,如果没有赋值就为undefined

3、promise中接受一个函数参数,参数中携带reject、resolve两个状态

4、promise可以包裹异步操作(也是对异步任务的封装,操作成功返回resolve,失败返回reject)

5、then放方法接受两个参数,而且两个参数都是函数类型的值,第一个参数是对象成功时候的回调,第二个参数是对象失败的时候回调

举个列子:

    <script>
      var p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("ok");
        }, 1000);
      });

      console.log(p1);
    </script>

创建了一个promise对象为p1,其在promsie中包裹了一个异步方法,我们把p1的状态改变为成功的,打印出p1看看内容是什么?

image-20211218134215300

可看出p1是一个promise对象,状态为resolve,值为ok

promsie中的方法

then

then方法是promise实列化的方法,它接受两个函数参数,分别对应着promise的成功、失败,是一个回调函数,可以通过拿到promise对应状态下的值,如果只有一个参数,则只返回成功的值,如下所示:

我们写一个按钮,点击后30%的概率弹出中奖提示:

HTML文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link href="./index.css" rel="stylesheet" />
  </head>

  <body>
    <div class="content">
      <button class="content_buton">
        <p class="content_p">抽奖</p>
      </button>
    </div>
    <script>
      function randomNum(m, n) {
        var num = Math.floor(Math.random() * (m - n) + n);
        return num;
      }

      let timer = null; //防抖操作,定义一个值

      const btn = document.getElementsByClassName("content_buton")[0];
      btn.addEventListener("click", () => {
        var promise = new Promise((resolve, reject) => {
          clearTimeout(timer); //清除掉上一次的值
          timer = setTimeout(() => {//这里不管点击多少次,都在一秒的时间内只执行一次
            const time = randomNum(1, 100);
            if (time <= 30) {
              resolve(time);
            } else {
              reject(time);
            }
          }, 1000);
        });
        promise.then(
          (value) => {
            console.log("value", value);
            alert("中奖了,你的数字为:", value);
          },
          (value) => {
            alert("再接再厉,你的数字为:", value);
          }
        );
      });
    </script>
  </body>
</html>
  

css文件

.content_buton {
  width: 100px;
  border-radius: 10px;
  background-color: #1e90ff;
  border: 0px;
  cursor: pointer;
}
.content_p {
  font-size: larger;
}

在这里如果成功做一个状态改变也就是调用resolve函数,失败就调用reject函数,然后用then函数做出对应的处理

上面的列子中还做出了简单的防抖操作。

注意:then返回的结果是一个promise对象,既然是promise对象,那有如下规则

1、若参数是非promise对象则反回的是成功的状态的值

2、若参数是promise对象,则返回的值由参数中promise决定

主要看接受的参数什么类型

如下列:

   <script>
      var p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(
            new Promise((resolve, reject) => {
              return reject("ok");
            })
          );
        }, 1000);
      });

      p1.then((v) => {
        console.log(v);
      });

      p1.catch((v) => {
        console.log(v);
      });
    </script>
catch

catch回调函数只能接受一个参数,传入的参数只能是promise的失败状态,用法和then差不多,把上面的列子改一下:

 <script>
      function randomNum(m, n) {
        var num = Math.floor(Math.random() * (m - n) + n);
        return num;
      }

      let timer = null;
      let baodi = 0;

      const btn = document.getElementsByClassName("content_buton")[0];
      btn.addEventListener("click", () => {
        var promise = new Promise((resolve, reject) => {
          clearTimeout(timer);
          timer = setTimeout(() => {
            const time = randomNum(1, 100);
            if (time <= 30) {
              resolve(time);
            } else {
              reject(time);
            }
          }, 1000);
        });
        promise.then((value) => {
          console.log("value", value);
          alert("中奖了,你的数字为:", value);
        });

        promise.catch((value) => {
          alert("再接再厉,你的数字为:", value);
        });
      });
    </script>
Promise.resolve

该方法不属于实列对象的方法,而是promise函数对象上的方法,规则如下:

1、若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象

2、如果传入的是promise对象,则参数的结果决定了resolve的结果

3、参数接受一个,非数组

作用:快速创建一个promise对象,而且可以指定一个封装的值

    <script>
      let p1 = Promise.resolve(111);
      //若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
      // 如果传入的是promise对象,则参数的结果决定了resolve的结果

      let p2 = Promise.resolve(
        new Promise((resolve, reject) => {
          // 这里就是内部的promise中reject的结果决定了promise.resolve的结果
          reject("ok");
        })
      );
      console.log(p1);
      console.log(p2);
    </script>

image-20211218141830616

在p2中我们可以看到,该方法下返回的是一个promise对象,返回的值也是由参数决定的

Promise.reject

该方法返回一个失败的promise对象,和resolve类似,但是不管参数是传入非promise对象还是promise对象,返回的都是一个失败的promise对象

    <script>
      let p1 = Promise.reject(111);
      //若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
      // 如果传入的是promise对象,则参数的结果决定了resolve的结果

      let p2 = Promise.reject(
        new Promise((resolve, reject) => {
          // 这里就是内部的promise中reject的结果决定了promise.resolve的结果
          resolve("ok");
        })
      );
      console.log(p1);
      console.log(p2);
    </script>

image-20211218142753256

作用就是快速的返回一个指定值失败的promsie对象

Promise.all

all方法和之前的方法不同,它的参数接收的是一个数组,Promise.all(value),value是一个包含n个promise的数组

1、all方法只有传入的数组都是resolve状态的才会返回成功,

2、若传入不成功的promsie则只返回第一个不成功的proimsie(就算失败的前有成功的状态,也只返回失败的值)

3、其中promsie执行的顺序会按照传入参数的promise顺序进行执行(p1比p2先传入,就算p1的运行时间比p2长,在都成功的情况下,也会先返回p1的结果)

在这里举列子中,resolve、reject的方法就派上用处了,快速的创建指定状态的promise对象

    <script>
      let p1 = Promise.reject("111");
      //若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
      // 如果传入的是promise对象,则参数的结果决定了resolve的结果

      let p2 = Promise.reject(
        new Promise((resolve, reject) => {
          // 这里就是内部的promise中reject的结果决定了promise.resolve的结果
          resolve("ok");
        })
      );

      let p3 = Promise.resolve("888");
      let p4 = Promise.resolve(
        new Promise((resolve, reject) => {
          resolve("hellow");
        })
      );

      console.log(
        "promise.all返回失败的状态",
        Promise.all([p3, p1, p3, p2, p4])
      );
      console.log("promise.all返回成功的状态", Promise.all([p3, p4]));
    </script>

image-20211218144020673

Promise.race

race和all接受的参数一样,需要是一个数组,但是它返回的值 是第一个完成的promise的状态的值 ,如下:

    <script>
      let p1 = Promise.reject("111");
      //若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
      // 如果传入的是promise对象,则参数的结果决定了resolve的结果

      let p2 = Promise.reject(
        new Promise((resolve, reject) => {
          // 这里就是内部的promise中reject的结果决定了promise.resolve的结果
          resolve("ok");
        })
      );

      let p3 = Promise.resolve("888");
      let p4 = Promise.resolve(
        new Promise((resolve, reject) => {
          resolve("hellow");
        })
      );

      let p5 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve();
        }, 1000);
      });

      console.log("promis.race返回的值", Promise.race([p5, p1, p2]));
      console.log("promis.all返回的值", Promise.all([p5, p3, p4]));
      console.log("promis.race返回的值", Promise.race([p2, p5, p2]));
    </script>

image-20211218145951810

可以看到返回的结果并不是根据排序来的,是根据先完成的值来的

promsie中的异常穿透

当我们使用很多的then去回调时,如果有throw抛出异常,我们可以用catch去拿到报错

  <script>
      let p1 = new Promise((resolve, reject) => {
        resolve();
      });

      p1.then(() => {
        throw "失败";
      })
        .then(() => {
          console.log(222);
        })
        .then(() => {
          console.log(33);
        })
        .catch((err) => {
          console.log(err);
        });
    </script>

实际上就是链式调用最后加上一个catch,第一个报错就会再catch中被拿到

在链式调用时,当promise的状态没改变的时候,函数不会下一步执行,会中断操作

Promsie自定义封装

index.js文件

function Promise(excutor) {
  this.PromiseStatus = "pending";
  this.PromiseResult = null;
  this.callback = [];
  const resolve = (data) => {
    if (this.PromiseStatus === "pending") {
      this.PromiseResult = data;
      this.PromiseStatus = "resolve";

      setTimeout(() => {
        this.callback.forEach((item) => {
          item.OnResolve(data);
        });
      });
    }
  };

  const reject = (data) => {
    if (this.PromiseStatus === "pending") {
      this.PromiseResult = data;
      this.PromiseStatus = "reject";
      setTimeout(() => {
        this.callback.forEach((item) => {
          item.OnReject(data);
        });
      });
    }
  };

  try {
    excutor(resolve, reject);
  } catch (e) {
    reject(e);
    console.log(reject(e));
  }

  excutor(resolve, reject);
}

Promise.prototype.then = function (OnResolve, OnReject) {
  // 这步允许只穿一个参数,可以把不传的另一个参数抛出
  if (typeof OnReject !== "function") {
    OnReject = (reason) => {
      throw reason;
    };
  }
  if (typeof OnResolve !== "function") {
    OnReject = (value) => {
      throw value;
    };
  }
  return new Promise((resolve, reject) => {
    //封装函数
    const callback1 = (type) => {
      try {
        let result = type(this.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (v) => {
              resolve(v);
            },
            (v) => {
              reject(v);
            }
          );
        } else {
          resolve(result);
        }
      } catch (e) {
        reject(e);
      }
    };
    //调用then的回调函数
    if (this.PromiseStatus === "resolve") {
      setTimeout(() => {
        callback1(OnResolve);
      });
    }
    if (this.PromiseStatus === "reject") {
      setTimeout(() => {
        callback1(OnReject);
      });
    }

    //判断pending的状态,保存回调函数
    if (this.PromiseStatus === "pending") {
      this.callback.push({
        OnResolve: () => {
          try {
            let result = OnResolve(this.PromiseResult);
            if (result instanceof Promise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (v) => {
                  reject(v);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        },

        OnReject: () => {
          try {
            let result = OnReject(this.PromiseResult);
            if (result instanceof Promise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (v) => {
                  reject(v);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        },
      });
    }
  });
};

Promise.prototype.catch = function (OnReject) {
  return this.then(undefined, OnReject);
};

//自定义Promise.resolve方法,感觉和then差不多

Promise.resolve = (value) => {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (v) => {
          resolve(v);
        },
        (v) => {
          reject(v);
        }
      );
    } else {
      resolve(value);
    }
  });
};

Promise.reject = (value) => {
  return new Promise((resolve, reject) => {
    return reject(value);
  });
};

Promise.all = (value) => {
  return new Promise((resolve, reject) => {
    console.log("111", value.length);
    let data = [];
    let temp = 0;
    for (let i = 0; i < value.length; i++) {
      value[i].then(
        (v) => {
          temp++;
          console.log(temp);
          data[i] = v;

          if (temp === value.length) {
            resolve(data);
          }
        },
        (v) => {
          reject(v);
        }
      );
    }
  });
};

Promise.race = (value) => {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < value.length; i++) {
      value[i].then(
        (v) => {
          resolve(v);
        },
        (v) => {
          reject(v);
        }
      );
    }
  });
};

HTML文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="./index.js"></script>
    <title>Document</title>
  </head>
  <body>
    <script>
      let p1 = new Promise((resolve, reject) => {
        // resolve("ok");
        reject("oo");
        // throw '错误'

        // 》》》》》》》》》》》  上面的都是同步的去改变对象的状态,下面举出异步的改变对象的状态

        // setTimeout(() => {
        //   reject("ok");
        // }, 1000);
      });
      //   console.log("p1", p1);
      //   const res = p1.then(
      //     (e) => {
      //       return "yhear";
      //     },
      //     (e) => {
      //       throw "o";
      //     }
      //   );

      let res = Promise.resolve(
        new Promise((resolve, reject) => {
          resolve("ooo");
        })
      );

      let a = Promise.resolve("ok");
      let b = Promise.reject("false");

      let result = Promise.race([b, a, res]);
      console.log(result);
    </script>
  </body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_43376559/article/details/122012915