Promise 实现 (从简易版到符合Promise A+规范)

news2024/10/7 8:31:57

前言

手写 Promise 是面试的时候大家都逃避的送命题,在学些了解后发现通过实现源码更能将新一代的异步方案理解的通透,知其然知其所以然的运用。

如果直接将源码贴到此处势必不能有更大的收获,下面就按实现版本来看做简要分析。

回顾 Promise

Promise 是 CommonJS 提出来的这一种规范,有多个版本,在 ES6 当中已经纳入规范,原生支持 Promise 对象,非 ES6 环境可以用类似 Bluebird、Q 这类库来支持。

Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅,还可以批量处理异步任务。

简单归纳下 Promise:三个状态、两个过程、一个方法,快速记忆方法:3-2-1

三个状态:pending、fulfilled、rejected

两个过程:

  • pending → fulfilled(resolve)
  • pending → rejected(reject)

一个方法:then

当然还有其他概念,如 catch、 Promise.all/race/allSettled。

基础版

基础测试用例

// 1. 链式调用
var p1 = new Promise(function (resolve, reject) {console.log("init Promise");if (Math.random() > 0.5) {resolve("大");} else {reject("小");}
});
p1.then((data) => console.log("success", data),(reason) => console.log("error", reason)
).then(() => console.log("success 2"),() => console.log("error 2")
);

// 2. 异步延时
var sleep = (time, data) =>new Promise(function (resolve, reject) {setTimeout(resolve, time, data);});
sleep(3000, "时间到!").then((val) => {console.log(val);
});

// 3. 状态变更后不可变
const p2 = new Promise(function (resolve, reject) {resolve("失败了!");reject("还会成功吗!");
});
p2.then((data) => console.log(data),(reason) => console.log(reason)
);

// Promise 打印日志:
// init Promise
// success 大 / error 小
// 失败了!
// success 2 /error 2
// 时间到!(延时 3 s) 

Promise 的基本特征

1.new promise 时, 需要传入一个立即执行函数 fn,fn 接受两个参数,分别是 resolve 和 reject;
2.promise 有三个状态:pending,fulfilled,or rejected, 默认状态是 pending,只能从 pending 到 rejected, 或者从 pending 到 fulfilled,状态一旦确认,就不会再改变;
3.promise 必须有一个 then 方法,then 接收两个参数,分别是 promise 成功的回调 fulfilledFn, 和 promise 失败的回调 rejectedFn;
4.promise then 支持链式调用。

手写基础版

class Promise {constructor(executor) {this.status = "pending";this.handleFulfilled = []; // 存储成功后的回调this.handleRejection = []; // 存储失败后的回调// ! resolve 形参的实际参数在这儿const resolve = (data) => {// 状态变更只有一次if (this.status !== "pending") {return;}this.status = "fulfilled";// ! 等一会,否则 handleFulfilled 为空setTimeout(() => {this.handleFulfilled.forEach((fn) => fn(data));}, 0);};const reject = (reason) => {if (this.status !== "pending") {return;}this.status = "rejected";setTimeout(() => {this.handleRejection.forEach((fn) => fn(reason));}, 0);};try {executor(resolve, reject);} catch (e) {// 遇到错误时,捕获错误,执行 reject 函数reject(e);}}then(fulfilledFn, rejectedFn) {this.handleFulfilled.push(fulfilledFn);this.handleRejection.push(rejectedFn);return this;}
} 

测试用例:

// 简易版 Promise 打印日志:
// init Promise
// success 大 / error 小
// success 2 /error 2 (x 未通过)
// 失败了!
// 时间到!(延时 3 s) 

存在的问题:

1.微任务宏任务队列打印顺序
2.then 链式调用不是通过返回 this,而是返回一个新的 promise
3.链式调用支持参数缺省
4.等等…

认识 Promise /A+ 规范

手写 Promise,需要遵守怎样的规则,业界所有 Promise 的类库都遵循 Promise/A+ 规范。译文

改进版

先按 Promise 的基本规范对上面的基础版进行改进。

1.new promise 时, 需要传入一个立即执行函数executorexecutor 接受两个参数,分别是 resolve 和 reject;
2.promise 有三个状态:pendingfulfilled,or rejected, 默认状态是 pending,只能从 pendingrejected, 或者从 pendingfulfilled,状态一旦确认,就不会再改变;「规范 Promise/A+ 2.1」
3.promise 有一个value保存成功状态的值,可以是undefined/thenable/promise;「规范 Promise/A+ 1.3」
4.promise 有一个reason保存失败状态的值;「规范 Promise/A+ 1.5」
5.promise 必须有一个 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected;「规范 Promise/A+ 2.2」
6.如果调用 then 时,promise 已经成功,则执行onFulfilled,参数是 promise 的value
7.如果调用 then 时,promise 已经失败,那么执行 onRejected, 参数是 promise 的 reason

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {constructor(executor) {this.status = PENDING; // 默认状态为 PENGINGthis.value = undefined; // 存放成功状态得值this.reason = undefined; // 存放失败状态得值this.handleFulfilled = []; // 存储成功后的回调this.handleRejection = []; // 存储失败后的回调// ! resolve 形参的实际参数在这儿const resolve = (data) => {// 状态变更只有一次if (this.status === PENDING) {this.status = FULFILLED;this.value = data;this.handleFulfilled.forEach((fn) => fn(data));}};const reject = (reason) => {if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;this.handleRejection.forEach((fn) => fn(reason));}};try {executor(resolve, reject);} catch (e) {// 遇到错误时,捕获错误,执行 reject 函数reject(e);}}then(onFulfilled, onRejected) {if (this.status === FULFILLED) {onFulfilled(this.value);}if (this.status === REJECTED) {onRejected(this.reason);}if (this.status === PENDING) {this.handleFulfilled.push(() => onFulfilled(this.value));this.handleRejection.push(() => onRejected(this.reason));}return this;}
} 

Promise A+规范版

规范思路梳理

如果调用 then 时,promise 已经成功,则执行 onFulfilled,并将 promise 的值作为参数传递进去。 如果 promise 已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。如果 promise 的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)

1.then 的参数 onFulfilledonRejected 可以缺省,如果 onFulfilled 或者 onRejected 不是函数,将其忽略,且依旧可以在下面的 then 中获取到之前返回的值;「规范 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2」
2.promise 可以 then 多次,promise 的 then 方法返回一个新的 promise 「规范 Promise/A+ 2.2.7」
3.如果 then 返回值 x 是一个普通值,那么就会把这个结果作为参数,传递给下一个 then 的成功的回调(onFulfilled)
4.如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调(onRejected) 规范 Promise/A+ 2.2.7.2」
5.如果 then 的返回值 x 是一个 promise,那么会等这个 promise 执行完,promise 如果成功,就走下一个 then 的成功;如果失败,就走下一个 then 的失败;如果抛出异常,就走下一个 then 的失败;「规范 Promise/A+ 2.2.7.3、2.2.7.4」
6.如果 then 的返回值 x 和 promise 是同一个引用对象,造成循环引用,则抛出异常,把异常传递给下一个 then 的失败的回调中;「规范 Promise/A+ 2.3.1」
7.如果 then 的返回值 x 是一个 promise,且 x 同时调用 resolve 函数和 reject 函数,则第一次调用优先,其他所有调用被忽略;「规范 Promise/A+ 2.3.3.3.3」

源码实现

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

// 将 onFufilled 的返回值进行判断取值处理,把最后获得的普通值放入最外面那层的 Promise 的 resolve 函数中
const resolvePromise = (promise2, x, resolve, reject) => {// 自己等待自己完成是错误的实现,用一个循环引用的类型错误,结束掉 promisePromise/A+ 2.3.1if (promise2 === x) {return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));}// 只能调用一次,为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)Promise/A+ 2.3.3.3.3let called;// 如果 x 不是null,是对象或者方法if ((typeof x === "object" && x != null) || typeof x === "function") {try {// 这个首先存储对 x.then 的引用,然后测试该引用let then = x.then;if (typeof then === "function") {// 那我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.definePropertyPromise/A+ 2.3.3.3// 第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调then.call(x,(y) => {// 根据 promise 的状态决定是成功还是失败if (called) return;called = true;// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1resolvePromise(promise2, y, resolve, reject);},(r) => {// 只要失败就失败 Promise/A+ 2.3.3.3.2if (called) return;called = true;reject(r);});} else {// 如果 x.then 是个普通值就直接返回 resolve 作为结果Promise/A+ 2.3.3.4resolve(x);}} catch (e) {// Promise/A+ 2.3.3.2if (called) return;called = true;reject(e);}} else {// 如果 x 是个普通值就直接返回 resolve 作为结果Promise/A+ 2.3.4resolve(x);}
};

class Promise {constructor(executor) {this.status = PENDING;this.value = undefined;this.reason = undefined;this.onResolvedCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {if (this.status === PENDING) {this.status = FULFILLED;this.value = value;this.onResolvedCallbacks.forEach((fn) => fn());}};let reject = (reason) => {if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());}};try {executor(resolve, reject);} catch (error) {reject(error);}}then(onFulfilled, onRejected) {//解决 onFufilled,onRejected 没有传值的问题//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获onRejected =typeof onRejected === "function"? onRejected: (err) => {throw err;};// 每次调用 then 都返回一个新的 promisePromise/A+ 2.2.7let promise2 = new Promise((resolve, reject) => {if (this.status === FULFILLED) {//Promise/A+ 2.2.2//Promise/A+ 2.2.4 --- setTimeout 宏任务模拟异步setTimeout(() => {try {//Promise/A+ 2.2.7.1// 因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断// 如果返回值为promise对象,则需要取出结果当作promise2的resolve结果// 如果不是,直接作为promise2的resolve结果let x = onFulfilled(this.value);// x可能是一个proimise// 抽离出一个公共方法来判断他们是否为promise对象resolvePromise(promise2, x, resolve, reject);} catch (e) {//Promise/A+ 2.2.7.2reject(e);}}, 0);}if (this.status === REJECTED) {//Promise/A+ 2.2.3setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(promise2, x, resolve, reject);} catch (e) {reject(e);}}, 0);}if (this.status === PENDING) {this.onResolvedCallbacks.push(() => {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(promise2, x, resolve, reject);} catch (e) {reject(e);}}, 0);});this.onRejectedCallbacks.push(() => {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(promise2, x, resolve, reject);} catch (e) {reject(e);}}, 0);});}});return promise2;}
} 

测试一下,打印日志也对了:

// Promise 打印日志:
// init Promise
// error 小
// 失败了!
// success 2
// 时间到!(延时 3 s) 

再试试 then 的链式调用和值的穿透:

const promise = new Promise((resolve, reject) => {reject("失败");
}).then().then().then((data) => {console.log(data);},(err) => {console.log("[Error]:", err); //log: [Error]: 失败}); 

完善 Promise API

虽然上述的 promise 源码已经符合 Promise/A+ 的规范,但是原生的 Promise 还提供了一些其他方法,如:

  • Promise.prototype.catch()
  • Promise.prototype.finally()
  • Promise.resolve(value) —— 使用给定 value 创建一个 resolved 的 promise。
  • Promise.reject(error) —— 使用给定 error 创建一个 rejected 的 promise。
  • Promise.all(promises) —— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成 Promise.all 的 error,所有其他 promise 的结果都会被忽略。
  • Promise.race(promises) —— 等待第一个 settle 的 promise,并将其 result/error 作为结果返回。
  • Promise.any(promises)(ES2021 新增方法)—— 等待第一个 fulfilled 的 promise,并将其结果作为结果返回。如果所有 promise 都 rejected,Promise.any 则会抛出 AggregateError 错误类型的 error 实例。
  • Promise.allSettled(promises)(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:* status: “fulfilled” 或 “rejected”* value(如果 fulfilled)或 reason(如果 rejected)。

下面具体说一下每个方法的实现:

Promise.prototype.catch

先从错误捕获开始,catch 用来捕获 promise 的异常,就相当于一个没有成功的 then。

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

测试例子:

// 抛出一个错误,大多数时候将调用catch方法
var p1 = new Promise(function (resolve, reject) {throw "Uh-oh!";
});

p1.catch(function (e) {console.log(e); // "Uh-oh!"
});

// 在异步函数中抛出的错误不会被catch捕获到
var p2 = new Promise(function (resolve, reject) {setTimeout(function () {throw "Uncaught Exception!";}, 1000);
});

p2.catch(function (e) {console.log(e); // 不会执行
}); 

Promise.prototype.finally()

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

Promise.prototype.finally = function (callback) {return this.then((value) => {return Promise.resolve(callback(value)).then(() => value);},(reason) => {return Promise.resolve(callback(reason)).then(() => {throw reason;});});
}; 

测试用例:

const promise1 = Promise.resolve(123);

promise1.then((value) => {return value;}).finally((res) => {console.log("finally", res); // finally 123});

Promise.resolve(456).finally(() => {return new Promise((resolve, reject) => {setTimeout(() => {resolve(123);}, 3000);});}).then((data) => {console.log(data, "success"); // 3秒后,456 'success'}).catch((err) => {console.log(err, "error");}); 

Promise.resolve

默认产生一个成功的 promise。Promise.resolve(value)方法返回一个以给定值解析后的 Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是 thenable(即带有"then" 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。此函数将类 promise 对象的多层嵌套展平。

static resolve(data){return new Promise((resolve,reject)=>{resolve(data);})
} 

如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在 constructor 方法中做一个小小的处理:

let reject = (reason) => {if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());}
};

let resolve = (value) => {// 如果 value 是一个promise,那我们的库中应该也要实现一个递归解析if (value instanceof Promise) {// 递归解析return value.then(resolve, reject);}if (this.status === PENDING) {this.status = FULFILLED;this.value = value;this.onResolvedCallbacks.forEach((fn) => fn());}
}; 

测试例子:

const promise1 = Promise.resolve(123);

promise1.then((value) => {console.log(value);// expected output: 123
});

Promise.resolve(new Promise((resolve, reject) => {setTimeout(() => {if (Math.random() > 0.5) {resolve("ok");} else {reject("err");}}, 3000);})
).then((data) => {console.log(data, "success"); // ok success}).catch((err) => {console.log(err, "error"); // err error}); 

Promise.reject()

默认产生一个失败的 promise,Promise.reject 是直接将值变成错误结果。

static reject(reason){return new Promise((resolve,reject)=>{reject(reason);})
} 

Promise.all()

promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。

Promise.all = function (values) {if (!Array.isArray(values)) {const type = typeof values;return new TypeError(`TypeError: ${type} ${values} is not iterable`);}return new Promise((resolve, reject) => {let resultArr = [];let orderIndex = 0;const processResultByKey = (value, index) => {resultArr[index] = value;if (++orderIndex === values.length) {resolve(resultArr);}};for (let i = 0; i < values.length; i++) {let value = values[i];if (value && typeof value.then === "function") {value.then((value) => {processResultByKey(value, i);}, reject);} else {processResultByKey(value, i);}}});
}; 

测试一下:

Promise.all([new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3
]).then(alert); // 1,2,3 当上面这些 promise 准备好时:每个 promise 都贡献了数组中的一个元素

Promise.all([new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Whoops!")), 2000)),new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).catch(alert); // Error: Whoops! 

Promise.race()

Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。

Promise.race = function (promises) {return new Promise((resolve, reject) => {// 一起执行就是for循环for (let i = 0; i < promises.length; i++) {let val = promises[i];if (val && typeof val.then === "function") {val.then(resolve, reject);} else {// 普通值resolve(val);}}});
}; 

这里第一个 promise 最快,所以它变成了结果。第一个 settled 的 promise “赢得了比赛”之后,所有进一步的 result/error 都会被忽略。

例如,这里的结果将是 1:

Promise.race([new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Whoops!")), 2000)),new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(alert); // 1 

Promise.any()

与 Promise.race 类似,区别在于 Promise.any 只等待第一个 fulfilled 的 promise,并将这个 fulfilled 的 promise 返回,它不会等待其他的 promise 全部完成。如果给出的 promise 都 rejected,那么则返回 rejected 的 promise 和 AggregateError 错误类型的 error 实例—— 一个特殊的 error 对象,在其 errors 属性中存储着所有 promise error。

例如,这里的结果将是 1:

Promise.any([new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Whoops!")), 1000)),new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(alert); // 1 

这里的第一个 promise 是最快的,但 rejected 了,所以第二个 promise 则成为了结果。在第一个 fulfilled 的 promise “赢得比赛”后,所有进一步的结果都将被忽略。

这是一个所有 promise 都失败的例子:

Promise.any([new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Ouch!")), 1000)),new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Error!")), 2000)),
]).catch((error) => {console.log(error.constructor.name); // AggregateErrorconsole.log(error.errors[0]); // Error: Ouch!console.log(error.errors[1]); // Error: Error!
}); 

正如你所看到的,我们在 AggregateError 错误类型的 error 实例的 errors 属性中可以访问到失败的 promise 的 error 对象。

实现该方法,可以通过上面的两个例子:

Promise.any 只要传入的 promise 有一个是 fullfilled 则立即 resolve 出去,否则将所有 reject 结果收集起来并返回 AggregateError

Promise.any = function (promises) {return new Promise((resolve, reject) => {promises = Array.isArray(promises) ? promises : [];let len = promises.length;// 用于收集所有 rejectlet errs = [];// 如果传入的是一个空数组,那么就直接返回 AggregateErrorif (len === 0)return reject(new AggregateError("All promises were rejected"));promises.forEach((promise) => {promise.then((value) => {resolve(value);},(err) => {len--;errs.push(err);if (len === 0) {reject(new AggregateError(errs));}});});});
}; 

Promise.allSettled()

如果任意的 promise reject,则 Promise.all 整个将会 reject。当我们需要 所有 结果都成功时,它对这种“全有或全无”的情况很有用:Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:

  • {status:“fulfilled”, value:result} 对于成功的响应,
  • {status:“rejected”, reason:error} 对于 error。
Promise.allSettled = function (promises) {const rejectHandler = (reason) => ({ status: "rejected", reason });const resolveHandler = (value) => ({ status: "fulfilled", value });const convertedPromises = promises.map((p) =>Promise.resolve(p).then(resolveHandler, rejectHandler));return Promise.all(convertedPromises);
}; 

测试一下:

Promise.allSettled([new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3
]).then(console.log); // [{status: 'fulfilled', value: 1}, {status: 'fulfilled', value: 2}, {status: 'fulfilled', value: 3}]

Promise.allSettled([new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),new Promise((resolve, reject) =>setTimeout(() => reject(new Error("Whoops!")), 2000)),new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(console.log); // [{status: 'fulfilled', value: 1}, {status: 'rejected', value: "Error: Whoops!"}, {status: 'fulfilled', value: 3}]测试一下: 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot测试类编写

前置要求: a.测试类上需要的注解 SpringBootTest AutoConfigureMockMvc Slf4j b.引入MockMvc类 Autowired private MockMvc mockMvc; c.如果需要前置条件可以用before注解 1.get/delete请求 // 查询Testvoid testQuery() throws Exception {String content mockMvc.perfor…

Django(15):身份和权限认证

目录1.Django中的身份认证模块1.1 用户模型1.2 认证模块1.3 项目搭建演示2.权限管理架构2.1 权限相关数据模型2.2 权限相关功能函数2.3 权限分配函数2.4 权限设置3.资源访问管理1.Django中的身份认证模块 1.1 用户模型 Django中有内建的用户模块django.contrib.auth.models.U…

2022 CNCC 中国计算机大会参会总结

前言 第 19 届 CNCC 于2022年12月8-10日召开&#xff0c;本届大会为期三天&#xff0c;首次采取全线上举办形式&#xff0c;主题为“算力、数据、生态”&#xff0c;重点在保持多样性、聚焦热点前沿话题、平衡学术界和产业界参与等维度展开讨论。大会由CCF会士、中国科学院院士…

【SpringBoot】一文带你入门SpringBoot

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;【Spring】 &#x1f96d;本文内…

【职场进阶】做好项目管理,先从明确职责开始

优秀的项目管理一定是高效协调各方资源、反馈及时、调整迅速的。 同时可以做到让参与各方在整个项目过程中张弛有序、愉快合作&#xff0c;最终实现产品项目的效益最大化。 那什么是项目呢&#xff1f; 项目是为向客户提供独特的产品或服务而进行的临时性任务&#xff0c;项目有…

TypeScript 对象key为number时的坑

首先在js的对象中有一个设定&#xff0c;就是对象的key可以是字符串&#xff0c;也可以是数字。 不论key是字符串还是数字&#xff0c;遍历对象key的时候&#xff0c;这个key会变成字符串 通过[] 操作符访问key对应值时候&#xff0c;不论是数字还是字符串都转成了 字符串的k…

Chromedriver安装教程

第一步 查看你当前Chrome浏览器的版本&#xff0c;如下图所示&#xff1a; 第二步 查看当前Chrome浏览器的版本号&#xff0c;如下图所示,版本 108.0.5359.125&#xff08;正式版本&#xff09; &#xff08;64 位&#xff09;中的&#xff0c;108就是我们的版本号。 第三…

VTK-PointPlacer

前言&#xff1a;本博文主要研究VTK中点转换到曲面上的应用&#xff0c;相关的接口为vtkPolygonalSurfacePointPlacer&#xff0c;为深入研究将基类vtkPointPlacer开始讲解。主要应用为在PolyData表面进行画线。 vtkPointPlacer 描述&#xff1a;将2D display位置转换为世界坐…

ospf知识点汇总

OSPF &#xff1a; 开放式最短路径优先协议使用范围&#xff1a;IGP 协议算法特点&#xff1a; 链路状态型路由协议&#xff0c;SPF算法协议是否传递网络掩码&#xff1a;传递网络掩码协议封装&#xff1a;基于IP协议封装&#xff0c;协议号为 89一.OSPF 特点1.OSPF 是一种典型…

基于javaweb(springboot+mybatis)网上酒类商城项目设计和实现以及文档报告

基于javaweb(springbootmybatis)网上酒类商城项目设计和实现以及文档报告 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏…

【Linux】Linux项目自动化构建工具—make/Makefile

目录一.什么是make/MakefileMakefilemake二.Makefile逻辑1.简单依赖2.复杂依赖三.make指令1.make的使用2.clean清理3.伪目标4.make如何确定是否编译访问时间的影响修改时间的影响一.什么是make/Makefile Makefile 在Windows下&#xff0c;我们使用VS、VS Code这些ide编写C/C程…

MySQL的客户端/服务器架构

以我们平时使用的微信为例&#xff0c;它其实是由两部分组成的&#xff0c;一部分是客户端程序&#xff0c;一部分是服务器程序。客户端可能有很多种形式&#xff0c;比如手机APP&#xff0c;电脑软件或者是网页版微信&#xff0c;每个客户端都有一个唯一的用户名&#xff0c;就…

赶紧收藏 | 50个超实用微信小程序,巨好用|||内含免费配音软件

现在App太多了&#xff0c;想用的功能都要下载&#xff0c;但是手机有258g内存不允许这么放肆呀&#xff0c;只能挖掘不占用存的方法了&#xff0c;小程序就解决了这个痛&#xff0c;节省内存&#xff0c;让手机不再卡顿&#xff0c;打游戏也舒服.给大家整理了50个很好用的小程…

【阶段三】Python机器学习11篇:机器学习项目实战:KNN(K近邻)回归模型

本篇的思维导图: 项目实战(KNN回归模型) K近邻算法回归模型则将离待预测样本点最近的K个训练样本点的平均值进行待预测样本点的回归预测。 项目背景 K近邻除了能进行分类分析,还能进行回归分析,即预测连续变量,此时的KNN称为K近邻回归模型。回归问题是一类…

synchronized 重量级锁分析

synchronized 重量级锁分析 1. 背景 在JDK1.6以前&#xff0c;synchronized 的工作方式都是这种重量级的锁。它的实现原理就是利用 kernel 中的互斥量,mutex。主要是内核中的mutex 能够保证它是一个互斥的量。如果线程1拿到了 mutex,那么线程2就拿不到了。这是内核帮我们保证…

二十三、Kubernetes中Pod控制器分类、ReplicaSet(RS)控制器详解

1、概述 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照pod的创建方式可以将其分为两类&#xff1a; 自主式pod&#xff1a;kubernetes直接创建出来的Pod&#xff0c;这种pod删除后就没有了&#xff0c;也不会重建 控制器创建的pod&#xff1a;kub…

小米应用商店APP侵权投诉流程

目录一、官方指引二、侵权投诉流程1.侵权投诉通知和反通知流程2.受理渠道3.权利人发起侵权通知邮件一、官方指引 https://dev.mi.com/distribute/doc/details?pId1142 二、侵权投诉流程 1.侵权投诉通知和反通知流程 2.受理渠道 对外邮箱&#xff1a;developerxiaomi.com …

云呐|固定资产盘点APP

如果工人想做好他们的工作&#xff0c;他们想做好他们的工作。目前&#xff0c;行政事业单位对固定资产管理进行一物一卡一码管理&#xff0c;根据条形码粘贴和扫码总结&#xff0c;是目前科学完善的总结方法&#xff0c;具有快速、高效、准确的特点。对于这种方法&#xff0c;…

5.6、TCP超时重传时间的选择

超时重传时间的选择是 TCP 最复杂的问题之一 1、超时重传时间RTO的选取 假设主机 A 给主机 B 发送 TCP 数据报文段 000&#xff0c;并记录下当前的时间 主机 B 收到后&#xff0c;给主机 A 发送相应的确认报文段 主机 A 收到确认报文段后&#xff0c;记录下当前的时间 那么…

为什么BI仪表板的共享功能对企业如此重要?

随着大数据的发展&#xff0c;企业逐渐将数据作为其决策的重要组成部分&#xff0c;共享商业智能 (BI) 仪表板显得越来越重要。例如&#xff0c;在员工之间共享BI仪表板&#xff0c;不仅可以做出更好的数据驱动决策&#xff0c;还可以更好地提高工作透明度和问责制&#xff0c;…