Promise 重写 (第一部分)

学习关键语句:
promise 重写

写在前面

重新学习了怎么重写 promise , 我觉得最重要的就是要有思路,不然有些 A+ 规范是完全想不到的

开始

重写函数的过程中, 最重要的是有思路
我们从哪里获取重写思路? 从正常的代码中
我们先看正常的代码

const p1 = new Promise((resolve, reject) => {
  resolve();
  console.log("1");
}).then(() => {
  console.log("2");
});

我们的思路就来了:

  1. promise 是一个类
  2. 初始化的时候传入的是一个函数, 函数里面有两个参数, resolve, reject
  3. 这说明类的构造器中有这两个函数声明
  4. 实例化后的每一个 promise 都有自己的状态, 说明有实例属性 state

想到这里马上开始重写, 完成以上 1\2\3\4 点

// 1. promise 是一个类
class MyPromise {
  constructor(executor) {
    // 4. 实例化后的每一个 promise 都有自己的状态, 说明有实例属性 state
    this.state = "pending";
    // 3. 这说明类的构造器中有这两个函数声明
    let resolve = () => {};
    let reject = () => {};
    // 2. 初始化的时候传入的是一个函数, 函数里面有两个参数, resolve, reject
    executor(resolve, reject);
  }
}
let p = new MyPromise((resolve, reject) => {});

接下来我们知道 resolve 和 reject 函数中有参数 value 和 reason
所以我们马上就又有思路了:

  1. 执行器函数中调用 resolve 会传入参数, reject 同样也是
  2. 需要一个 then 方法来链式调用, 所以 then 方法是一个原型方法
  3. then 方法中有两个参数, 一个成功回调一个失败回调

完成以上 5\6\7 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    // 5. 执行器函数中调用 resolve 会传入参数, reject 同样也是
    let resolve = (value) => {};
    let reject = (reason) => {};

    executor(resolve, reject);
  }
  // 6. 需要一个 then 方法来链式调用, 所以 then 方法是一个原型方法
  // 7. then 方法中有两个参数, 一个成功回调一个失败回调
  then(onFulfilled, onRejected) {}
}
let p = new MyPromise((resolve, reject) => {});

我们知道只有在构造器函数中调用了 resolve 或者 reject 方法后才会进入 then 方法, 同时我们也知道一旦调用 resolve 或者 reject 方法就会改变 promise 实例的状态, 所以我们接下来的思路就是:

  1. 调用 resolve 或者 reject 方法后改变状态
  2. 成功或者失败回调需要拿到对应的 resolve 和 reject 函数中的参数, 这需要我们在实例上新增相应的属性存储
  3. 在 resolve 和 reject 方法中要赋值给实例属性
  4. 在 then 方法中判断状态调用方法

完成以上 8\9\10\11 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    // 9. 成功或者失败回调需要拿到对应的 resolve 和 reject 函数中的参数, 这需要我们在实例上新增相应的属性存储
    this.value = undefined;
    this.reason = undefined;
    let resolve = (value) => {
      // 8. 调用 resolve 或者 reject 方法后改变状态
      this.state = "fulfilled";
      // 10. 在 resolve 和 reject 方法中要赋值给实例属性
      this.value = value;
    };
    let reject = (reason) => {
      // 8. 调用 resolve 或者 reject 方法后改变状态
      this.state = "rejected";
      // 10. 在 resolve 和 reject 方法中要赋值给实例属性
      this.reason = reason;
    };

    executor(resolve, reject);
  }
  then(onFulfilled, onRejected) {
    // 11. 在 then 方法中判断状态调用方法
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    }
    if (this.state === "rejected") {
      onRejected(this.reason);
    }
  }
}
let p = new MyPromise((resolve, reject) => {});

好完成到这里的时候, 不涉及异步的操作我们已经完成了, 现在我们尝试使用一下我们重写的 promise

const p1 = new MyPromise2((resolve, reject) => {
  resolve(2);
  console.log("1");
}).then((value) => {
  console.log(value);
});

在这里插入图片描述

接下来考虑异步的问题, 即如果执行器函数中存在异步的话, 进入 then 方法时状态其实还是 pending , 所以需要考虑当进入 then 方法时状态为 pending 就是异步, 我们要将异步时候的回调函数保存起来当真正执行的时候再调用 , 由于同一个 promise 可以多次调用 then 方法 , 所以需要用一个数组来保存所有可能的回调函数 , 我们从这出发 , 给出以下思路:

  1. 在实例上添加属性来保存成功回调和失败回调
  2. 在 then 方法中遇到 pending 状态就是有异步情况 , 真正的执行回调将不在这里发生 , 存到实例的属性上

完成以上 12\13 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    // 12. 在实例上添加属性来保存成功回调和失败回调
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
    };

    executor(resolve, reject);
  }
  then(onFulfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    }
    if (this.state === "rejected") {
      onRejected(this.reason);
    }
    // 13. 在 then 方法中遇到 pending 状态就是有异步情况, 真正的执行回调将不在这里发生, 存到实例的属性上
    if (this.state === "pending") {
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}
let p = new MyPromise((resolve, reject) => {});

那么真正的回调函数执行到底在哪里呢? 我们来看一下一个出现了异步的情况代码, 看以下代码:

由于构造器函数中出现一个定时器, 所以会在 1 秒后才打印出 2

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2);
  }, 1000);
  console.log("1");
}).then((value) => {
  console.log(value);
});

上面这个代码中能执行回调函数的地方只有在 resolve 函数中了, 所以我们就又有思路了:

  1. 在 resolve 和 reject 方法中执行回调函数

完成以上 14 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
      // 14. 在 resolve 和 reject 方法中执行回调函数
      this.onFulfilledCallbacks.forEach((fn) => fn());
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
      // 14. 在 resolve 和 reject 方法中执行回调函数
      this.onRejectedCallbacks.forEach((fn) => fn());
    };

    executor(resolve, reject);
  }
  then(onFulfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    }
    if (this.state === "rejected") {
      onRejected(this.reason);
    }
    if (this.state === "pending") {
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}
let p = new MyPromise((resolve, reject) => {});

我们知道 promise 实例是可以链式调用的, 这意味每一个方法的最后都会返回一个新的 promise 来提供给下一次调用 promise 原型方法, 所以我们就考虑到在 then 方法中直接返回一个新的 promise 对象, 同时, 由于执行器函数执行是同步代码, 所以我们可以直接将 then 中的代码放入到新的 promise 中, 但是我们知道链式调用 then 方法时我们可以拿到上一次 then 返回的值, 所以在成功回调返回的值, 我们将再次使用新的 promise 中的 resolve 方法去调用, 以下就是这次的思路:

  1. 在 then 方法中新声明一个 promise 对象并返回, 将 then 方法中代码放入执行器函数中
  2. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 和 reject 方法

完成以上 15\16 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach((fn) => fn());
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
      this.onRejectedCallbacks.forEach((fn) => fn());
    };

    executor(resolve, reject);
  }
  then(onFulfilled, onRejected) {
    // 15. 在 then 方法中新声明一个 promise 对象并返回, 将 then 方法中代码放入执行器函数中
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        // 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法
        x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.state === "rejected") {
        // 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法
        x = onRejected(this.reason);
        reject(x);
      }
      if (this.state === "pending") {
        this.onFulfilledCallbacks.push(() => {
          // 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法
          x = onFulfilled(this.value);
          resolve(x);
        });
        this.onRejectedCallbacks.push(() => {
          // 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法
          x = onRejected(this.reason);
          reject(x);
        });
      }
    });
    return p2;
  }
}
let p = new MyPromise((resolve, reject) => {});

好的这样就已经完成了链式调用的问题了, 但是现在又有一个新的问题了, 那就是如果第一次的回调函数返回的是一个 promise 对象呢? 那就需要对 x 进行判断和处理, 我们思路如下:

  1. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
  2. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
  3. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
  4. 同样的, 在构造器中, 对执行器函数使用 try.catch 包裹

完成以上 17\18\19\20 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach((fn) => fn());
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
      this.onRejectedCallbacks.forEach((fn) => fn());
    };
    // 20. 同样的, 在构造器中, 对执行器函数使用 try.catch 包裹
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        // 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
        setTimeout(() => {
          // 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
          try {
            x = onFulfilled(this.value);
            // 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "rejected") {
        // 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
        setTimeout(() => {
          // 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
          try {
            x = onRejected(this.reason);
            // 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "pending") {
        this.onFulfilledCallbacks.push(() => {
          // 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
          setTimeout(() => {
            // 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
            try {
              x = onFulfilled(this.value);
              // 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          // 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
          setTimeout(() => {
            // 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
            try {
              x = onRejected(this.reason);
              // 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
      }
    });
    return p2;
  }
}
// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
function resolvePromise(p2, x, resolve, reject) {
  if (p2 === x) {
    return reject(new Error("Error: p2 is x"));
  }
}
let p = new MyPromise((resolve, reject) => {});

接下来我们要继续完善 resolvePromise 中的逻辑, 我们需要判断 x 到底是不是 promise 对象, 我们使用是否是带 then 属性的对象来判断, 是就是, 不是就不是, 当是 promise 对象后, 直接调用 x 的 then 方法, 在成功回调和失败回调中分别调用 resolvePromise 传入的 resolve 和 reject

  1. x 需要是个对象或者函数
  2. x 为普通数据类型就直接 resolve
  3. 获取 x 的 then 属性, 是函数才是 promise 对象
  4. 获取 x.then 可能会报错, 使用 try.catch 包裹

完成以上 21\22\23\24 点

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach((fn) => fn());
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
      this.onRejectedCallbacks.forEach((fn) => fn());
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            x = onFulfilled(this.value);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            x = onRejected(this.reason);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "pending") {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onFulfilled(this.value);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onRejected(this.reason);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
      }
    });
    return p2;
  }
}
function resolvePromise(p2, x, resolve, reject) {
  if (p2 === x) {
    return reject(new Error("Error: p2 is x"));
  }
  // 21. x 需要是个对象或者函数
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 24. 获取 x.then 可能会报错, 使用 try.catch 包裹
    try {
      // 23. 获取 x 的 then 属性, 是函数才是 promise 对象
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            resolve(y);
          },
          (r) => {
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (err) {
      reject(err);
    }
  } else {
    // 22. x 为普通数据类型就直接 resolve
    resolve(x);
  }
}
let p = new MyPromise((resolve, reject) => {});

好, 这样看起来已经很完美了, 返回的是 promise 对象我们也能解决了, 但是紧接着又有一个问题, 要是里面的 promise 再返回一个 promise 呢? 我们就想到用递归来解决这个问题, 同时我们还要解决一个问题, 那就是一个 promise 的执行器函数中只会执行遇到的第一个 resolve 或者 reject 方法, 我们用新增变量来避免重复调用

  1. 当每次返回的都是 promise 对象时, 我们递归调用判断 x 的类型的函数
  2. 声明一个变量来防止构造器函数中重复执行 resolve 和 reject 方法
  3. 每次进入 then 方法时, 如果没有参数, 就直接返回一个返回参数的函数
class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach((fn) => fn());
    };
    let reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;
      this.onRejectedCallbacks.forEach((fn) => fn());
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    // 27. 每次进入 then 方法时, 如果没有参数, 就直接返回一个返回参数的函数
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            x = onFulfilled(this.value);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            x = onRejected(this.reason);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.state === "pending") {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onFulfilled(this.value);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onRejected(this.reason);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
      }
    });
    return p2;
  }
}
function resolvePromise(p2, x, resolve, reject) {
  // 26. 声明一个变量来防止构造器函数中重复执行 resolve 和 reject 方法
  let called = false;
  if (p2 === x) {
    return reject(new Error("Error: p2 is x"));
  }
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // 25. 当每次返回的都是 promise 对象时, 我们递归调用判断 x 的类型的函数
            resolvePromise(p2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (err) {
      if (called) return;
      called = true;
      reject(err);
    }
  } else {
    resolve(x);
  }
}
let p = new MyPromise((resolve, reject) => {});

总结

ok 这样就重写完了最基本的 promise , 之后我们再来补充上 promise 上面的方法包括 catch \ race \ all 等等

我觉得虽然挺难的 , 但是如果你有思路的话,顺着思路写下来其实是没问题的

结束

学习之路漫漫啊~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/149679.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高阶智驾必上「激光雷达」,一场车企的集体投票

‍作者 | 张祥威 编辑 | 德新 2023年尾上市的这一批车型中,以问界新M7、理想MEGA、小鹏X9、智界S7和极氪007最为典型,它们的头顶大多搭载了一颗激光雷达,有的车型比如小鹏X9,甚至在前大灯位置配置了两颗激光雷达。 这是为实现高…

谷粒商城项目-环境配置

安装vegrant 2.2.18 注意vritual box(6.1.30)和vegrant版本兼容 初始化和创建虚拟机 vagrant init centos/7 vagrant up连接虚拟机 vegrant ssh解决vagrant up速度过慢问题 https://app.vagrantup.com/centos/boxes/7/versions/2004.01直接下载对应镜像…

零基础学会酒店预订小程序制作

" 如果你想要开发一个酒店预订小程序,以下是一个简单的步骤指南,帮助你通过第三方制作平台/工具如乔拓云网来实现这一目标: 1. 找一个合适的第三方制作平台/工具: 在如今的市场上,有许多第三方制作平台/工具可供选…

Java中的继承

文章目录 前言一、为什么需要继承二、继承的概念三、继承的语法四、父类成员访问4.1子类中访问父类的成员变量1.子类和父类不存在同名成员变量2.子类和父类成员变量同名 4.2子类中访问父类的成员方法1.成员方法名字不同2.成员方法,名字相同 五、super和this关键字六…

场景图形管理 - (2)

裁剪平面示例(二) 裁剪平面(osg::Scissor)示例(二)的代码如程序清单8-2所示 // 裁剪平面测试&#xff08;2&#xff09; void scissor_8_2(const string strDataFolder) { osg::ref_ptr<osgViewer::Viewer> viewer new osgViewer::Viewer(); osg::ref_ptr<osg::Gra…

FPGA电平标准的介绍

对FPGA的管脚进行约束的时候&#xff0c;常常看到这样的电平标准&#xff0c;例如LVCOM18&#xff0c;LVCOS25&#xff0c;LVDS&#xff0c;LVDS25等等&#xff0c;其实这些都是一系列的电平标准。 针对数字电路而言&#xff0c;数字电路表示电平的只有1和0两个状态&#xff0c…

11.15 知识总结(模板层、模型层)

一、 模板层 1.1 过滤器 1.什么是过滤器&#xff1f; 过滤器类似于python的内置函数&#xff0c;用来把变量值加以修饰后再显示。 2. 语法 1、 {{ 变量名|过滤器名 }} 2、链式调用&#xff1a;上一个过滤器的结果继续被下一个过滤器处理 {{ 变量名|过滤器1|过滤器2 }} 3、有的过…

栈与队列:用队列实现栈

目录 题目&#xff1a; 栈和队列的数据模型对比&#xff1a; 思路分析&#xff1a; 代码分析&#xff1a; 一、定义栈 二、初始化栈 三、入栈 四、出栈⭐ 代码解析&#xff1a; 五、获取栈顶元素 六、 判断栈是否为空 七、销毁栈 完整代码&#xff1a; 需…

在webstorm中配置sass编译环境

1.下载ruby 下载地址&#xff1a;ruby下载 2.安装ruby 下载之后&#xff0c;有一个exe安装包 双击exe文件 &#xff0c;并选择自己的安装位置&#xff08;这个位置一定要记得&#xff0c;需要在webstorm中使用&#xff09;。其他的步骤默认安装即可。 3.安装sass ruby安装成功后…

记feign调用第三方接口时header是multipart/form-data

1.请求第三方接口&#xff0c;用feign请求 请求第三方接口&#xff0c;用feign请求&#xff0c;header不通&#xff0c;feign的写法不同 调用时报错Could not write request: no suitable HttpMessageConverter found for request type [com.ccreate.cnpc.mall.dto.zm.ZMPage…

程序员的护城河:技术深度、创新精神与软实力的完美结合

文章目录 1. 技术深度&#xff1a;建立坚实的技术基石2. 创新精神&#xff1a;应对变革的利器3. 软实力&#xff1a;沟通协作构筑团队防线4. 结合三者构筑完美护城河 &#x1f389;程序员的护城河&#xff1a;技术深度、创新精神与软实力的完美结合 ☆* o(≧▽≦)o *☆嗨~我是I…

【Maven】进阶

文章目录 1. 聚合2. 继承3. 属性变量定义与使用4. 版本管理5. 资源配置6. 多环境配置7. 跳过测试&#xff08;了解&#xff09; 1. 聚合 为了防止某个模块&#xff08;dao&#xff09;更新了&#xff0c;重新编译了&#xff0c;导致和其他模块不兼容&#xff0c;需要用一个roo…

移植freertos到qemu上运行

1、freertos源码下载 参考博客&#xff1a;《freertos源码下载和目录结构分析》&#xff1b; 2、编译freertos 2.1、选择合适的Demo freertos官方已经适配过qemu&#xff0c;所以我们并不需要做源码级别的移植&#xff0c;只需要选择合适的Demo文件夹。 2.2、修改Makefile 2.3…

sCrypt 发布零知识证明精选列表

sCrypt 发布了与零知识证明相关的精选列表&#xff0c;包括&#xff1a;教程&#xff0c;编程语言&#xff0c;工具&#xff0c;书籍&#xff0c;社区&#xff0c;证明系统。欢迎收藏 github 代码仓&#xff1a;https://github.com/sCrypt-Inc/awesome-zero-knowledge-proofs。…

spark与scala的对应版本查看

仓库地址 https://mvnrepository.com/artifact/org.apache.spark/spark-core 总结 spark3.0 以后&#xff0c;不再支持 scala2.11spark3.0 以后&#xff0c;只能用 scala2.12以上

Zabbix邮箱告警

1.在邮箱中获取授权码 2.zabbix配置 agengt配置 添加以下配置 [rootserver03 ~]# visudo zabbix ALL(ALL) NOPASSWD: ALL [rootserver03 ~]# vim /etc/zabbix/zabbix_agentd.conf EnableRemoteCommands1 #允许接收远程命令 修改原有的值&#xff0c;不要在末…

Windows系统CMake+VS编译protobuf

目录 一些名词CMake构建VS工程下载protobuf源码下载CMake编译QT中使用 方案二失败&#xff1a;CMakeQT自带的Mingw编译参考链接 一些名词 lib dll lib库实际上分为两种&#xff0c;一种是静态链接lib库或者叫做静态lib库&#xff0c;另一种叫做动态链接库dll库的lib导入库或称…

.Net8 Blazor 尝鲜

全栈 Web UI 随着 .NET 8 的发布&#xff0c;Blazor 已成为全堆栈 Web UI 框架&#xff0c;可用于开发在组件或页面级别呈现内容的应用&#xff0c;其中包含&#xff1a; 用于生成静态 HTML 的静态服务器呈现。使用 Blazor Server 托管模型的交互式服务器呈现。使用 Blazor W…

LINMP搭建wordpress-数据库不分离

目录 一、nginx部署 1.安装nginx前的系统依赖环境检查 2.下载nginx源代码包 3.解压缩源码包 4.创建普通的nginx用户 5.开始编译安装nginx服务 6.创建一个软连接以供集中管理 7.配置nginx环境变量 二、mysql 1.创建普通mysql用户 2.下载mysql二进制代码包 3.创建mys…

吴恩达《机器学习》8-5->8-6:特征与直观理解I、样本与值观理解II

8.5、特征与直观理解I 一、神经网络的学习特性 神经网络通过学习可以得出自身的一系列特征。相对于普通的逻辑回归&#xff0c;在使用原始特征 x1​,x2​,...,xn​ 时受到一定的限制。虽然可以使用一些二项式项来组合这些特征&#xff0c;但仍然受到原始特征的限制。在神经网…