最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Promise基础到手写(使用+构造)

    正文概述 掘金(coolFish)   2021-04-10   467

    Promise简介

    Promise 最早出现是为了解决编程中的异步行为导致的回调地狱。在没有 Promise 之前,对于函数的异步行为,一般采用回调函数的方式,在一个函数调用结束触发回调函数,这样会导致多层级的回调函数,难以维护。 Promise 有两个参数,一个成功回调,一个失败回调,所以在 Promise 外部,可以准确的获得成功和失败的时机,并且 Promise 支持链式调用,这样可以方便的进行多次调用,但是永远都是单层级,便于维护。

    两种异步行为对比

    回调地狱方式

    var sayhello = function (name, callback) {
      setTimeout(function () {
        console.log(name);
        callback();
      }, 1000);
    }
    sayhello("first", function () {
      sayhello("second", function () {
        sayhello("third", function () {
          console.log("end");
        });
      });
    });
    //输出: first second third  end
    

    Promise 写法

    let sayHello = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("first");
        resolve();
      }, 1000);
    });
    sayHello
      .then(() => {
        console.log("second");
      })
      .then(() => {
        console.log("third");
      })
      .then(() => {
        console.log("end");
      });
    

    我们可以发现 Promise 不管有多少次逻辑处理,每一次只有一层,清晰可见,不像回调一样,层层嵌套,难以理清。

    Promise 基础

    Promise 实例

    let promise = new Promise(
      function(resolve, reject) { 	// executor(执行者)
      	setTimeout(()=>{		
        resolve("done"),1000);
    });
    

    我们只需要 new Promise即可创建一个 Promise,创建即立刻调用其中的执行者。 executor 接受两个参数:resolve 和 reject 。这些是JavaScript 引擎预定义的,不要我们创建,我们只需要在我们想要告知的状态中调用对应的方法即可。

    let promise = new Promise(function(resolve, reject) {
      resolve("done");
    
      reject(new Error("…")); // 被忽略
      setTimeout(() => resolve("…")); // 被忽略
    });
    

    这儿只能有一个结果或一个 error executor 只能调用一个 resolve 或一个 reject。任何状态的更改都是最终的。 所有其他的再对 resolvereject 的调用都会被忽略 并且,resolve/reject 只需要一个参数(或不包含任何参数),并且将忽略额外的参数

    那么我们会疑问,我们费这么大工夫,在 Promise 内部做这么多操作,最后使他产生一个状态是为了什么,他失败与否和我们之前的回调地狱有什么关系?

    state 和 result 都是内部的
    Promise 对象的 state 和 result 属性都是内部的。
    我们无法直接访问它们。但我们可以对它们使用 .then/.catch/.finally 方法。
    我们在下面对这些方法进行了描述。
    

    上面我们使用 Promise 生产了一个成功或者失败的结果,可以通过使用 .then.catch 和 .finally 方法为消费函数进行结果接收。

    then

    let promise = new Promise(function(resolve, reject) {
      setTimeout(() => resolve("done!"), 1000);
    });
    
    // resolve 运行 .then 中的第一个函数
    promise.then(
      result => alert(result), // 1 秒后显示 "done!"
      error => alert(error) // 不运行
    );
    

    .then 的第一个参数是一个函数,该函数将在 promise resolved 后运行并接收结果。 .then 的第二个参数也是一个函数,该函数将在 promise rejected 后运行并接收 error。 如果我们只对成功完成的情况感兴趣,那么我们可以只为 .then 提供一个函数参数:

    let promise = new Promise(resolve => {
      setTimeout(() => resolve("done!"), 1000);
    });
    
    promise.then(alert); // 1 秒后显示 "done!"
    

    .catch(f) 调用是 .then(null, f) 的完全的模拟,它只是一个简写形式。简单说,catch就是一个接收错误结果的方法。 我们可以对 settled 的 promise 附加处理程序 如果 promise 为 pending 状态,.then/catch/finally 处理程序(handler)将等待它。否则,如果 promise 已经是 settled 状态,它们就会运行 自测案例 写一个 3s 后弹窗的 Promise

    function delay(ms) {
      // 你的代码
      return new Promise(resolve,reject){
      	setTimeout(resolve,3000)
      }
     }
    
    delay(3000).then(() => alert('runs after 3 seconds'));
    

    catch

    如果只要失败回调,那么只需要将 then 的第一个参数设置为null, 也可以使用 catch ,这里面可以接受 reject 的结果

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => reject(new Error("Whoops!")), 1000);
    });
    
    // .catch(f) 与 promise.then(null, f) 一样
    promise.catch(alert); // 1 秒后显示 "Error: Whoops!"
    
    

    finally

    无论结果如何,最后都会执行这里面的函数。 **finally()** 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。 这避免了同样的语句需要在then()catch()中各写一次的情况。

    构建 Promise

    在我了解完 Promise 后,我对如何实现他非常感兴趣,于是我试着自己来构建一个 Promise。 首先我们要分析一下我们的需求,我们要得到什么,要实现哪些功能,确定目标。

    1. 我们要实现一个名叫 Promise 的类。
    2. 类里我们要实现一个 resolve 成功通知。
    3. 类里我们要实现一个 reject失败通知。
    4. executor 立刻执行。
    5. 我们还要实现一个可以拿到结果的 then。
    6. 一个捕获错误的 catch。
    7. 一个不管结果的 finally。

    Promise基础到手写(使用+构造) 我们按照上图,分为两个大步骤,开始进行实现我们自己的 Promise。

    首先构造 Promise 类。

    1. 初始化阶段,我们考虑到 Promise 一共有三种状态,两个结果。所以我们要初始化状态和结果。
    2. 然后我们发送成功和失败的信息时,要改变状态,并且保存结果。
    class Promise {
      constructor(executor) {
        if (typeof executor !== "function") {
          console.log("参数不合法");
        } else {
          this.status = "pending";
          this.value = undefined;
          this.error = undefined;
          //自动运行Promise中的函数
          try {
            //将resolve和reject函数给使用者
            executor(resolve, reject);
          } catch (e) {
            //如果在函数中抛出异常则将它注入reject中
            reject(e);
          }
        }
      }
      resolve(data) {
        //成功触发
        if (this.status === "pending") {
          this.status = "fulfilled";
          this.value = data;
        }
      }
      reject(data) {
        //失败触发
        if (this.status === "pending") {
          this.status = "rejected";
          this.error = data;
        }
      }
      then() {}
      catch() {}
      finally() {}
    }
    
    

    我们实现了上述几个目标,接下来我们要实现接受结果信息的方法。

    1. then 接受两个参数,第一个将在 promise resolved 后运行并接收 value 。第二个将在 promise reject 后运行并接收 error。
    2. catch 只接受一个函数,promise reject 后运行并接收 error。
    3. finally 无论结果如何都会执行。
    class Promise {
      constructor(executor) {
        if (typeof executor !== "function") {
          console.log("参数不合法");
        } else {
          this.status = "pending";
          this.value = undefined;
          this.error = undefined;
          //自动运行Promise中的函数
          try {
            //将resolve和reject函数给使用者
            executor(this.resolve, this.reject);
          } catch (e) {
            //如果在函数中抛出异常则将它注入reject中
            this.reject(e);
          }
        }
      }
      resolve = (data) => {
        //成功触发
        if (this.status === "pending") {
          this.status = "fulfilled";
          this.value = data;
        }
      };
      reject = (data) => {
        //失败触发
        if (this.status === "pending") {
          this.status = "rejected";
          this.error = data;
        }
      };
      then(onFulfilled, onRejected) {
        if (this.status === "fulfilled") {
          onFulfilled(this.value);
        }
        if (this.status === "rejected") {
          onRejected(this.error);
        }
      }
      catch(onRejected) {
        if (this.status === "rejected") {
          onRejected(this.error);
        }
      }
      finally(onFinally) {
        if (this.status !== "pending") {
          onFinally();
        }
      }
    }
    
    

    这样,我们就完成了一个简易版的 Promise。 我们来将文件引入测试一下,看看结果如何。

    <!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>
      </head>
      <body>
        <script src="./index.js"></script>
        <script>
          let promise = new Promise((resolve, reject) => {
            resolve("coolFish!");
          });
          promise.then(
            (data) => {
              console.log("成功" + data);
            },
            (data) => {
              console.log("失败" + data);
            }
          );
        </script>
      </body>
    </html>
    
    
    

    结果和 Promise 一样,可以实现成功和失败的不同操作,接下来我们要开始扩展它的功能,从可支持链式调用开始。

    Promise.then

    首先我们明确 promise.then(onFulfilled, onRejected ) 做的事情

    1. 入参判断,处理 onFulfilled 或者 onRejected 不是函数的情况。
    2. 创建并且返回一个 promise 实例。
    3. 将 onFulfilled 和 onRejected 添加到事件队列(根据 promise 的状态来决定如何处理)。
    4. 状态为 fulfilled 执行 onFulfilled 。
    5. 状态为 rejected 则执行 onRejected。
    6. 如果没有做出决议,则添加进事件队列。
      then(onFulfilled, onRejected) {
        //创建并返回一个Promise实例
        return new Promise((resolve, reject) => {
          let wrapOnFulfilled = () => {
            setTimeout(() => {
              try {
                console.log("wrapOnFulfilled");
                let x = onFulfilled(this.value);
                resolve(x);
              } catch (error) {
                reject(error);
              }
            }, 0);
          };
          let wrapOnRejected = () => {
            setTimeout(() => {
              try {
                console.log("wrapOnRejected");
                let x = onRejected(this.error);
                resolve(x);
              } catch (error) {
                reject(error);
              }
            }, 0);
          };
          if (this.status === "fulfilled") {
            wrapOnFulfilled();
          } else if (this.status === "rejected") {
            wrapOnRejected();
          } else {
            this.onFulfilledCallbacks.push(wrapOnFulfilled);
            this.onRejectedCallbacks.push(wrapOnRejected);
          }
        });
      }
    
    

    Promise.all

    首先我们先明确目标 Promise.all 接受一个 promise 数组作为参数(从技术上讲,它可以是任何可迭代的,但通常是一个数组)并返回一个新的 promise。 当所有给定的 promise 都被 settled 时,新的 promise 才会 resolve,并且其结果数组将成为新的 promise 的结果。 我们来看一张流程图,然后我们按照流程图来实现我们的代码

    Promise基础到手写(使用+构造)

    all(promises) {
        return new Promise((resolve, reject) => {
          // 如果Promise.all接收到的是一个空数组([]),它会立即决议。
          if (!promises.length) {
            resolve([]);
          }
          let result = [];
          let resolvedPro = 0;
          for (let index = 0, length = promises.length; index < length; index++) {
            Promise.resolve(promises[index]).then(
              (data) => {
                // 注意,这里要用index赋值,而不是push。因为要保持返回值和接收到的promise的位置一致性。
                result[index] = data;
                if (++resolvedPro === length) {
                  resolve(result);
                }
              },
              (error) => {
                reject(error);
              }
            );
          }
        });
      }
    
    

    Promise.race

    // 需要注意的是,如果Promise.race接收到的是一个空数组([]),则会一直挂起,而不是立即决议。
    Promise.race = function(promises) {
      return new Promise((resolve, reject) => {
        promises.forEach((promise) => {
          Promise.resolve(promise).then(resolve, reject);
        });
      });
    };
    
    

    Promise.allSettled

    // Promise.allSettled 返回一个在所有给定的promise都已经fulfilled或rejected后的promise,
    // 并带有一个对象数组,每个对象表示对应的promise结果。
    Promise.allSettled = function(promises) {
      return new Promise((resolve, reject) => {
        if (!promises.length) {
          resolve([]);
        }
    
        let result = [];
        let resolvedPro = 0;
        for (let index = 0, length = promises.length; index < length; index++) {
          Promise.resolve(promises[index])
            .then((data) => {
              // 注意,这里要用index赋值,而不是push。因为要保持返回值和接收到的promise的位置一致性。
              result[index] = {
                status: FULFILLED_STATE,
                value: data,
              };
              if (++resolvedPro === length) {
                resolve(result);
              }
            })
            .catch((error) => {
              result[index] = {
                status: REJECTED_STATE,
                reason: error,
              };
              if (++resolvedPro === length) {
                resolve(result);
              }
            });
        }
      });
    };
    
    

    起源地下载网 » Promise基础到手写(使用+构造)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元