20230512----重返学习-Promise进阶-项目环境的问题-async与await

45 篇文章 0 订阅
8 篇文章 0 订阅

day-068-sixty-eight-20230512-Promise进阶-项目环境的问题-async与await

Promise进阶

  • 创建Promise实例对象的方案
    1. 方案一:new Promise()构造函数,都会返回一个全新的Promise实例对象。
      • 用new执行。
      • 返回一个Promise实例对象。
    2. 方案二:每一次执行旧Promise实例对象.then()方法,都会返回一个全新的新Promise实例对象
      • let 新Promise实例对象 = 旧Promise实例对象.then(onfulfilled,onrejected);
      • 返回一个全新的新Promise实例对象
        • 新Promise实例对象的状态和值,和旧Promise实例对象没有关系,和onfulfilled与onrejected这两个回调函数中的任意一个方法执行的细节有关。
          • 首先看方法执行是否报错,如果报错了,则新Promise实例对象是失败状态,值是报错原因。
          • 如果没有报错,再看方法执行的返回值,别名返回值X
            • 如果返回的是一个新的返回Promise实例对象
              • 如果返回值X旧Promise实例对象是同一个对象,则新Promise实例对象的状态是失败的,值就是抛出的异常。

                let p1 = new Promise(function(resolve){resolve(222)}).then()
                let p2 = p1.then(()=>{return p1})//caught SyntaxError: Identifier 'p1' has already been declared
                
              • 如果返回值X是一个标准的Promise官方实例对象,则新Promise实例对象的状态和值就是返回值X的状态和值。

              • 还需要判断返回值X是否是一个对象或者函数,因为它可能是一个伪Promise实例对象。

              • 如果是伪Promise实例。

                • 访问伪Promise实例。
                  • 如果报错了,则让新Promise实例对象的状态是失败状态,值就是报错原因。
                  • 这个是因为使用了类似于defineProperty方法对一个对象做了get劫持。
                  • 如果不报错。
                  • 如果then是一个函数,把then方法执行。
                  • 函数中的this需要是返回值X
                  • then.call(x)或者x.then()。
                  • 那么传递给该then方法传递onfulfilled/onrejected;
                  • 如果then不是函数,则说明返回值X不是Promise实例。那么新Promise实例对象的状态是成功状态,值就是返回值。
            • 具体可看Promises的A+规范。
            • 如果不是新的实例,则新Promise实例对象是成功状态,值就是返回值。
      • 这个就是可以让then可以链式运行。
        • Promise实例对象玩的核心主要就是then()链。
    3. Promise.reject()这一类,快捷创建Promise实例。
    4. Promise.all()这一类,创建计算后得到的的新相关Promise实例。
    5. 把函数用async修饰,返回值就是一个Promise实例对象。
  • then的穿透顺延机制
    • 每一次执行then方法的时候,如果需要传递的onfulfilled与inrejected并没有传递,则会把上一个实例的状态和值,顺延给下一个实例,就是执行then返回的这个实例。

      let p1 = Promise.resolve(100);
      let p2 = p1.then();
      p2.then(
        (value) => {
          console.log(`value-->`, value);
        },
        (reason) => {
          console.log(`reason-->`, reason);
        }
      );
      
      • 原理:

        let p1 = Promise.reject(100);
        let p2 = p1.then(
          (value) => {
            return value;
          },
          (reason) => {
            throw reason;
          }
        );
        p2.then(
          (value) => {
            console.log(`value-->`, value);
          },
          (reason) => {
            console.log(`reason-->`, reason);
          }
        );
        
        • 给then执行一些默认方法。
    • 平时写代码,then一般只传递onfulfilled,最后基于catch实现失败的处理。

      • 而且不论上述那个环节出现失败,都会走到最后的catch中。
        • 如果出现失败的Promise实例对象,但是没有对失败进行处理,则控制台会抛出红色警告,此警告不影响后续代码执行。
  • Promise.all()与Promise.any()这一类代码。
    • 创建一个总的Promise实例对象,别名P
    • 总实例的状态和值,取决于包含多个promise实例对象的集合promises中每个实例的状态和值。
      • all()方法:每个实例的状态都为成功,总实例的状态才为成功,值是一个数组,按照顺序存储了每个实例成功的值。
        • 其中只要有一个实例的状态是失败,则总实例就是失败,值就是此失败实例的值,后续的实例都不处理了!
      • any()方法:只要有一个成功,整体就是成功,值也是成功这一项的值,后续的也不管了。
        • 只有所有实例都失败,总实例才为失败,值是一句证明都失败的话。
      • race方法:那个实例先处理完毕,总实例的状态和值就按照它的来。
    • Promise集合中的每一项,都必须是Promise实例,如果出现一项不是,则默认会把其转换为Promise实例
      • 状态是成功的,值是本身。
    • 要求promises这个集合,需要是符合迭代器规范的集合Symbol.iterator

手写的源码

(() => {
  "use strict"; //使用严格模式,让JavaScript的自由度缩小,进而让代码更规范。

  // 思路来源于: [promise A+规范 英文官网](https://promisesaplus.com/)

  // 为对象设置不可枚举的属性;
  var define = function define(obj, key, value) {
    Object.defineProperty(obj, key, {
      writable: true,
      configurable: true,
      enumerable: false,
      value: value,
    });
  };

  // 确保是Promise类的实例
  var ensureInstance = function ensureInstance(obj) {
    if (!(obj instanceof Promise)) {
      // 必须要在Promise实例上被调用-不能被call或bind改为其它的对象。
      throw new TypeError(
        "Method Promise.prototype.then called on incompatible receiver " + obj
      );
    }
  };

  // 验证是否为Promise实例对象-包括伪Promise实例对象。
  var isPromise = function isPromise(x) {
    if (x !== null && /^(object|function)$/.test(typeof x)) {
      var then;
      try {
        then = x.then;
      } catch (err) {
        // console.log(`err-->`, err);
        return false;
      }

      // 返回值x是一个promise实例-伪实例或者是thenable
      if (typeof then === "function") {
        return true;
      }
    }
    return false;
  };

  // 基于定时器模拟queueMicrotask创建异步微任务-不过这里创建的是异步宏任务
  var queueTask = function queueTask(callback) {
    // 原则上还需要判断callback是否是个函数,不过这个方法只在内部使用,不用做太多检测。
    if (typeof queueMicrotask !== "undefined") {
      queueMicrotask(callback);
      return;
    }
    var timer = setTimeout(function () {
      callback();
      clearTimeout(timer);
    }, 0);
  };

  // 构造函数
  var Promise = function Promise(executor) {
    // console.log(new.target)
    // console.log("this-->", this);

    var self = this;

    // 得用new执行
    if (!(self instanceof Promise)) {
      throw new TypeError(
        "Promise constructor cannot be invoked without 'new'"
      );
      return;
    }

    // 得传入一个函数
    if (typeof executor !== "function") {
      throw new TypeError("Promise resolver undefined is not a function");
      return;
    }

    // 为实例设置私有属性:状态和值
    // self.state = "pending";
    // self.result = undefined;

    define(self, "state", "pending");
    define(self, "result", undefined);

    // 为实例设置私有属性:成功回调函数集合、失败回调函数集合。
    define(self, "onfulfilledCallback", []);
    define(self, "onrejectedCallback", []);

    // console.log(`self-->`, self);
    // 修改实例状态和值
    var change = function change(state, result) {
      // 状态一旦被更改后,则后续就不能再被更改了。
      if (self.state !== "pending") {
        return;
      }

      self.state = state;
      self.result = result;

      // 通知集合中的方法执行-通过异步微任务的方式执行
      // self.then(self.onfulfilled, self.onrejected);
      queueTask(function () {
        var callbacks =
          self.state === "fulfilled"
            ? self.onfulfilledCallback
            : self.onrejectedCallback;

        callbacks.forEach(function (callback) {
          callback(self.result);
        });
      });
    };

    try {
      // 立即执行executor函数
      executor(
        function resolve(value) {
          change("fulfilled", value);
        },
        function reject(reason) {
          change("rejected", reason);
        }
      );
    } catch (err) {
      // 如果executor函数执行报错,也会把实例的状态改为失败,值就是报错原因。
      change("rejected", err);
    }
  };

  // 原型对象
  var proto = Promise.prototype;

  // promise = "要返回的promise实例对象",
  // x = "返回值",
  // resolve = "返回值",
  // reject = "返回值",
  var Resolve = function Resolve(promise, x, resolve, reject) {
    // 根据返回值x,来决定新返回的实例promise的状态和值。

    if (x === promise) {
      throw new TypeError("新返回的promise实例对象不能循环");
    }

    if (x !== null && /^(object|function)$/.test(typeof x)) {
      var then;
      try {
        then = x.then;
      } catch (err) {
        reject(err);
        return;
      }

      // 返回值x是一个promise实例-伪实例或者是thenable
      if (typeof then === "function") {
        var called = false; //有没有被执行。
        try {
          then.call(
            x,
            function resolvePromise(y) {
              if (called) {
                return;
              }

              called = true;

              // resolve(y);//不能直接这样写,因为x成功后的值y,还可能是promise实例。如果y是一个新实例,则他决定了最终的结果 -- 递归处理y。
              Resolve(promise, y, resolve, reject);
            },
            function rejectPromise(r) {
              if (called) {
                return;
              }
              called = true;

              reject(r);
            }
          );
        } catch (err) {
          if (called) {
            return;
          }
          called = true;

          reject(err);
        }
        return;
      }
    }

    // 返回值绝对不是一个promise实例,则让then返回的新实例:状态是成功,值就是返回值x;
    resolve(x);
  };

  // 设置then()方法。
  define(proto, "then", function then(onfulfilled, onrejected) {
    var self = this;
    ensureInstance(self);

    if (typeof onfulfilled !== "function") {
      onfulfilled = function (value) {
        return value;
      };
    }
    if (typeof onrejected !== "function") {
      onrejected = function (reason) {
        throw reason;
      };
    }

    var promise = new Promise(function (resolve, reject) {
      switch (self.state) {
        case "fulfilled":
          queueTask(function () {
            try {
              var x = onfulfilled(self.result);
              Resolve(promise, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case "rejected":
          queueTask(function () {
            try {
              var x = onrejected(self.result);
              Resolve(promise, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        default:
          // self.onfulfilled=onfulfilled
          // self.onrejected=onrejected

          self.onfulfilledCallback.push(function (result) {
            // 传递的是匿名函数,但匿名函数可以通过作用域链拿到promise,  resolve, reject。
            // 不放进匿名函数中,则只能拿到x。

            try {
              var x = onfulfilled(result);
              Resolve(promise, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          self.onrejectedCallback.push(function (result) {
            try {
              var x = onrejected(result);
              Resolve(promise, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
      }
    });
    return promise;
  });
  define(proto, "catch", function (onfulfilled) {
    var self = this;
    ensureInstance(self);

    return self.then(null, onfulfilled);
  });

  // 静态私有属性方法
  define(Promise, "resolve", function resolve(x) {
    var promise = new Promise(function (resolve, reject) {
      Resolve(promise, x, resolve, reject);
    });
    return promise;
  });
  define(Promise, "reject", function reject(x) {
    return new Promise(function (_, reject) {
      reject(x);
    });
  });
  define(Promise, "all", function all(promises) {
    return new Promise(function (resolve, reject) {
      // 入参不是可迭代对象
      if (!Array.isArray(promises)) {
        throw new TypeError("promises is not array");
      }

      var num = 0;
      var result = [];

      // 如果集合中一项实例都没有,则整体也是成功,值是空数组。
      if (promises.length === 0) {
        resolve([]);
        return;
      }

      // 迭代集合中的每一项,观察每一项的状态和值---每一轮都产生了一个闭包
      promises.forEach(function (item, index) {
        // 必须保证每一项都是Promise实例对象。
        if (!isPromise(item)) {
          item = Promise.resolve(item);
        }

        item
          .then(function (value) {
            // 有一项为成功,就记录一项。
            num++;
            result[index] = value; //索引要记录为入参的对应索引。

            // 都成功了,现是整体返回的实例就是成功。
            if (num >= promises.length) {
              resolve(result);
            }
          })
          .catch(function (reason) {
            // 有一项失败,则整体返回的Promise实例对象的状态就是失败。
            reject(reason);
          });
      });

      // for(var i=0;i<promises.length;i++){
      //   (function(i){
      //   })(i)
      // }
    });
  });
  define(Promise, "any", function any(promises) {
    return new Promise(function (resolve, reject) {
      // 入参不是可迭代对象
      if (!Array.isArray(promises)) {
        throw new TypeError("promises is not array");
      }

      if (promises.length === 0) {
        reject(new Error("AggregateError: All promises were rejected"));
        return;
      }

      var num = 0;
      promises.forEach(function (item, index) {
        if (!isPromise(item)) {
          item = Promise.resolve(item);
        }

        item
          .then(function (value) {
            // 有一项为成功了,现是整体返回的实例就是成功。
            resolve(result);
          })
          .catch(function (reason) {
            // 有一项为失败,就记录一项。
            num++;
            if (num >= promises.length) {
              reject("都失败了");
            }
          });
      });

      // for(var i=0;i<promises.length;i++){
      //   (function(i){
      //   })(i)
      // }
    });
  });

  // 加测试-插件要求-插件为promises-aplus-tests。
  Promise.deferred = function deferred() {
    var result = {};
    result.promise = new Promise(function (resolve, reject) {
      result.resolve = resolve;
      result.reject = reject;
    });
    return result;
  };

  // Promise.resolve(100)
  // Promise.resolve(Promise.reject(-1))
  // Promise.reject(0)
  // Promise.reject(Promise.resolve(100))

  // 暴露API-让其支持各种环境
  if (typeof window !== "undefined") {
    window.Promise = Promise;
  }

  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = Promise;
  }
})();

一些好玩项目

项目环境的问题

  • 跑环境也就是按照 package.json 中的开发依赖和生产依赖,把需要的模块都安装一遍。
    • 一般就是项目所需的各种第三方插件,如jQuery、vue这类的。
    • 也有一些是开发工具。
      • 如scss等预编译工具。
  • 有根目录中看到对应的lock文件后,就用对应的包管理工具。
    • /package.lock 用npm。
      • npm install
    • /yarn.lock 用yarn。
      • yarn
    • /pnpm-lock.yaml 用pnpm。
      • pnpm install
  • 运行脚本命令,一般独立于环境安装。即可以跑环境用yarn,运行命令使用npm。
    • pnpm
      • pnpm
    • yarn
      • yarn 脚本命令
    • npm
      • npm run 脚本命令
  • 要学会看英文文档。
    • 看英文文档时。
      • 最好是先看一下全文中文翻译。
      • 之后看英文文档配合翻译工具来做。
        • 一般是一段英文一段地来看。
      • 然后是看英文文档带猜配合翻译工具。
        • 一般是单独看单个单词,其它的基本上都理解了。
      • 最后是看英文文档自己理解。
    • 原因:
      • 看特定行业,一般得用英文文档,中文文档一般是二手的,或者没充分测试,可能有坑。
    • 前端新规范:
      • 规范之前是草案,先看草案去猜。
      • 规范之后是有官方接口,同时也有官方文档。
      • 尝试用英文文档去看,之后用。
    • 1到3年看一遍ES6官方离线文档。
      1. ES6官方文档。
        • 当前目录有离线版本的。
      2. MDN文档。
      3. w3c英文官网。
      4. 掘金小册。
      5. stackoverflow。
      6. github。

async与await

  • async与await:简化promise操作的语法糖 promise+generator的语法糖
    • async的作用:async是用来修饰函数的。
      1. 让函数的返回值变为一个Promise实例对象。

        • 方法执行中遇到await,会等待await执行的处理结果。
          • 这一步是异步微任务的,会执行其它同步代码。
        • 方法执行报错,则返回失败的Promise实例对象,值是报错原因。
        • 方法执行不报错,方法执行结束时看方法执行的返回值。
          • 如果不是一个新的实例,则返回的实例是成功,值是返回值。
          • 如果返回的是一个新的实例,则返回的实例和这个新实例保持同步。
      2. 想要在函数中使用await,则当前函数必须经过async的修饰。

        let result = await Promise实例对象
        
        • await后面要跟一个Promise实例对象。
          • await fn()先执行fn函数,执行的返回值作为await等待的操作。
          • 如果不放,它会自动把后面的值处理为成功状态的Promise实例对象的返回值。
            • await 10
        • 如果后面的实例是成功状态。
          • result存储的就是成功的结果。
          • 当前上下文中,下面的代码可以继续执行。
        • 如果后面的实例是失败状态,则下面的代码就不会执行了。
          • 下面代码不会执行,并且抛出一个不影响后续操作的错误。
    • await会等待一个Promise实例对象的处理结果
      • 当await后面的实例是失败的,而且没有对失败进行处理,会抛出异常,这样就导致fn执行返回的实例也是失败的状态,值就是抛出的异常。
      • await也是一个异步的微任务
        • 它仅仅是在函数中,模拟出了同步的效果,它本身是异步的。
        • 只要遇到await,await后面的代码都会变成一个微任务,等待await后面的实例为成功时,才会把它放到EventQueue中的微任务队列中。
          • 此时async函数的同步代码就相当于暂时执行完了,先执行async函数后面的同步代码。

进阶参考

  1. [ES6官方离线文档](./ES6官方离线文档----ECMAScript 2015 Language Specification – ECMA-262 6th Edition.mhtml)
  2. stackoverflow
  3. .msi和.exe 文件的区别
  4. msi和exe安装文件有什么区别
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值