关于 Promise 你需要知道的一切
Promise
是 JavaScript 中的对异步操作最佳的 API 之一。作为JavaScript开发人员,需要熟练掌握 Promise
相关的知识。本文就来总结一下这些知识点,先来回顾 JavaScript 过去异步操作的处理方式?然后详细介绍 Promises 对象及相关的方法。
现在先来了解一下 JavaScript 异步概念。
异步回顾
什么是异步?如果在函数返回的时候,调用者还不能获取到预期结果,而是将来通过一定的方式得到(例如回调函数),这函数就是异步。
异步回调
异步回调,就是常见的 callback
,这是过去在 JavaScript 中处理异步操作的常见方式,如 AJAX ,发起一个HTTP请求,具体服务器什么时候返回响应数据取决于客户的环境,存在很多不确定因素,这时采用回调函数可以在返回响应数据的时候触发回调函数。
这种方式当有多个请求的情况,后续请求需要等待前面请求的响应数据的时候,就会出现常见的回调地狱。
const asyncMessage = function (message, callback) { setTimeout(function () { console.log(message); callback(); }, 1000); }; asyncMessage("title", function () { asyncMessage("cate", function () { asyncMessage("content", function () { console.log("detail"); }); }); });
出现回调地狱将会给项目开发带来很多问题:
-
会导致逻辑混乱,耦合性高,改动一处就会导致全部变动,嵌套多时,BUG问题难以发现。
-
不能使用
try...catch
来捕获异常。 -
不能使用
return
返回真实数据
为了避免回调地狱的出现,后来就有了 Promise
对象。
Promise 的工作方式
promise
对象是一个可以从异步函数同步返回的对象,它将处于 3 种可能的状态之一:
fulfilled
已兑现: onFulfilled()
将被调用,即操作完成(例如,resolve()
被调用)rejected
已拒绝: onRejected()
将被调用,即操作失败(例如,reject()
被调用)pending
待定:初始状态,既没有被兑现,也没有被拒绝
如果一个 promise
没有挂起(它已被解决或拒绝),它就会被解决。有时使用已解决和已解决表示同一件事:不是 pending
。
promise
一旦确定,就不能再重新确定,再次调用 resolve()
或 reject()
将没有效果。一个已确定的 promise
的具有不可变性。
对于 promise
的状态监控可以使用承诺链,即在 fulfilled
已兑现的状态可以使用 then
方法可以获取已兑现的结果,在rejected
已拒绝状态使用 catch
方法获取拒绝的原因。
const myPromise = new Promise(myExecutorFunc) .then(handleFulfilledA) .then(handleFulfilledB) .then(handleFulfilledC) .catch(handleRejectedAny);
看起来比 callback
的方式优雅一点,对于需要发起多次HTTP请求才能完整呈现的需求,代码如下:
const getPost = () => fetch("https://jsonplaceholder.typicode.com/posts/1"); const getAuthor = (id) => fetch("https://jsonplaceholder.typicode.com/users/" + id); const getComment = (id) => fetch("https://jsonplaceholder.typicode.com/users/" + id); getPost() // #1.fetch post .then((postResponse) => postResponse.json()) // #2. get & return post json .then((postResponse) => getAuthor(postResponse.id) // #3. fetch author .then((authorResponse) => authorResponse .json() // #4 get & return author json .then((authorResponse) => getComment(postResponse.id) // #5 fetch comment .then((commentResponse) => commentResponse.json()) // #6 get & return comment json .then((commentResponse) => { // #7 time to combine all results return { postResponse, authorResponse, commentResponse, }; // #8 combine & return all reponses }) ) ) .then((results) => { // #9 read all responses console.log(results.postResponse); console.log(results.authorResponse); console.log(results.commentResponse); }) ) .catch((error) => console.log(error)); // # 10 error handling
上面代码是否有种似曾相识的感觉,原本是为了解决回调地狱,但似乎理想跟现实还是有差距。
于是 ES2021 为 Promise 对象增加新的特征,其中包括:Promise.any()
、Promise.all()
、Promise.allSettled()
、Promise.race()
。
Promise.any()
Promise.any(promises)
能够并行运行 promise
,并解析为 promises
列表中第一个成功解析的 promise
的值。需要注意的是 Promise.any()
方法依然是实验性的,尚未被所有的浏览器完全支持。
下面来看看 Promise.any()
是如何工作的。
1.工作原理
Promise.any()
可用于以并行和竞争方式执行独立的异步操作,以获取任何第一个完成的 promise
的值。
该函数接受一个 promise
数组(通常为一个可迭代对象)作为参数,如下:
const anyPromise = Promise.any(promises);
当输入 promises
中的第一个 promise
被执行完成时,anyPromise
会立即解析为该 promise
的值。
可以使用 then
方法提取第一个 promise
的值:
anyPromise.then((firstValue) => { firstValue; // 第一个 promise 完成后返回的值 });
也可以使用 async/await
语法:
const firstValue = await anyPromise; console.log(firstValue); // 第一个 promise 完成后返回的值
Promise.any()
返回的 promise
与任何第一个执行的 promise
一起执行。即使某些 promise
被 rejected
,这些 rejections
也将会被忽略。
但是,如果输入数组中的所有 promises
都被拒绝,或者输入数组为空,那么 Promise.any()
会 rejected
包含输入的 promises
执行的 rejection
错误原因集合。
2. 使用指南
现在来深入介绍一下 Promise.any()
, 在这之前,先来定义 2 个简单的函数。
函数
resolveTimeout(value, delay)
将返回一个在经过delay
时间后有resolve
的promise
。
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); }
函数
rejectTimeout(reason, delay)
将返回一个在经过delay
时间后有reject
的promise
。
function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); }
接下来使用上面定义的2个辅助函数来试试 Promise.any()
。
2.1 完成所有 promises
下面尝试运行第一个解析列表:
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); } function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); } const fruits = ["potatoes", "tomatoes"]; const vegetables = ["oranges", "apples"]; const promise = Promise.any([ resolveTimeout(fruits, 1000), resolveTimeout(vegetables, 2000), ]); // 等待... const list = async () => { const result = await promise; console.log(result); }; // 1 秒之后 list(); // ['potatoes', 'tomatoes']
promise .any([…])
返回一个在 1秒内
解析到数组 fruits
的 promise
,因为解析fruits的 promise
先执行完成。
第二个是 2秒内
解析到数组 vegetables
的 promise
,其值将被忽略。
2.2 一个 promise
被 rejected
将上面第一个 promise
出现异常被 rejected
,如下代码:
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); } function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); } const vegetables = ["oranges", "apples"]; const promise = Promise.any([ rejectTimeout(new Error("fruits is empty"), 1000), resolveTimeout(vegetables, 2000), ]); // 等待... const list = async () => { const result = await promise; console.log(result); }; // 2 秒之后 list(); // [ 'oranges', 'apples' ]
上面的代码,第一个 promise
在 1秒后
被rejected
,从执行的结果不难看出 Promise.any()
跳过了第一个被rejected
的promise
,等待第二个 2秒后
执行完成的promise
。
2.3 所有的 promises
被 rejected
下面来看下当所有的 promises
被 rejected
会出现什么结果,如下代码:
function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); } const promise = Promise.any([ rejectTimeout(new Error("fruits is empty"), 1000), rejectTimeout(new Error("vegetables is empty"), 2000), ]); // 等待... const list = async () => { try { const result = await promise; console.log(result); } catch (aggregateError) { console.log(aggregateError); console.log(aggregateError.errors); } }; list(); // [AggregateError: All promises were rejected]
从上面代码的执行结果来看,当所有输入promises
被 rejected
后, Promise.any([...])
将返回一种特殊的错误 AggregateError
而被 rejected
,而详细的 rejected
原因在属性 aggregateError.errors
中 。
小结
Promise.any()
可用于以竞争方式并行执行独立的异步操作,以获取任何第一个成功执行完成的 promise
的值。如果 Promise.any()
的所有输入 promise
都被rejected
后,那么辅助函数返回的 promise
也会以错误集合的方式拒绝,该错误在一个特殊属性 AggregateError
中包含输入 promise
的拒绝原因:aggregateError.errors
。
Promise.all()
方法 Promise.all(promises)
,能够一次并行处理多个 promise
,并且只返回一个 promise
实例, 那个输入的所有 promise
的 resolve
回调的结果是一个数组。
下面来看看 Promise.all()
是如何工作的。
1.工作原理
Promise.all()
是一个内置的辅助函数,接受一组 promise
(或者一个可迭代的对象),并返回一个promise
:
const allPromise = Promise.all([promise1, promise2, ...]);
可以使用 then
方法提取第一个 promise
的值:
allPromise.then((values) => { values; // [valueOfPromise1, valueOfPromise2, ...] });
也可以使用 async/await
语法:
const values = await allPromise; console.log(values); // [valueOfPromise1, valueOfPromise2, ...]
Promise.all()
返回的 promise
被解析或拒绝的方式。
如果 allPromise
都被成功解析,那么 allPromise
将使用一个包含各个 promise
已执行完成后的值的数组作为结果。数组中 promise
的顺序是很重要的——将按照这个顺序得到已实现的值。
但是如果至少有一个 promise
被 rejected
,那么 allPromise
会以同样的原因立即 rejected
(不等待其他 promise
的执行)。
如果所有的 promise
被 rejected
,等待所有的promise
执行完成,但只会返回最先被rejected
的promise
的 reject
原因。
2. 使用指南
现在来深入介绍一下 Promise.all()
, 在这之前,先来定义 2 个简单的函数。
函数
resolveTimeout(value, delay)
将返回一个在经过delay
时间后有resolve
的promise
。
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); }
函数
rejectTimeout(reason, delay)
将返回一个在经过delay
时间后有reject
的promise
。
function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); }
接下来使用上面定义的2个辅助函数来试试 Promise.all()
。
2.1 完成所有 promises
下面定义了一个 promise
数组 allPromise
,所有的 promise
都能够成功的 resolve
值,如下:
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); } const fruits = ["potatoes", "tomatoes"]; const vegetables = ["oranges", "apples"]; const allPromise = [ resolveTimeout(fruits, 2000), resolveTimeout(vegetables, 1000), ]; const promise = Promise.all(allPromise); // 等待... 2秒后 const list = async () => { try { const result = await promise; console.log(result); } catch (error) { console.log(error.errors); } }; list(); // [ [ 'potatoes', 'tomatoes' ], [ 'oranges', 'apples' ] ]
从上面执行的结果来看 Promise.all()
返回的 promise
的 resolve
数组是按照执行前 allPromise
的顺序组成其结果。
promise
数组的顺序直接影响结果的顺序,和promise
执行完成的先后无关。
2.2 一个 promise
被 rejected
将上面数组 allPromise
的第一个 promise
出现异常被 rejected
,如下代码:
const promise = Promise.all([ rejectTimeout(new Error("fruits is empty"), 5000), resolveTimeout(vegetables, 1000), ]); // 等待... const list = async () => { try { const result = await promise; console.log(result); } catch (error) { console.log(error); } }; list(); // Error: fruits is empty
然而,在经过 5秒
之后,第一个 promise
由于异常被 rejected
,使得 allPromise
也被 rejected
,并返回跟第一个 promise
一样的错误信息:Error: fruits is empty
,即使在 1秒
后就完成的第二个 promise
的值也不被采纳。
接下来将数组 allPromise
的所有 promise
都抛出异常被 rejected
,通过定时器将 rejected
的顺序做个调整,如下:
const promise = Promise.all([ rejectTimeout(new Error("fruits is empty"), 5000), rejectTimeout(new Error("vegetables is empty"), 1000), ]); // 等待... const list = async () => { try { const result = await promise; console.log(result); } catch (error) { console.log(error); } };
经过 5秒
之后完成执行,而结果显示为 Error: vegetables is empty
,不难看出 allPromise
被 rejected
的原因是最先 rejected
的promise
。
Promise.all()
的这种行为被称为快速失败,如果promise
数组中至少有一个promise
被rejected
,那么返回的promise
也被拒绝。如果promise
数组中所有的都被rejected
,那么返回的promise
被拒绝的原因是先rejected
的那一个。
小结
Promise.all()
是并行执行异步操作并获取所有 resolve
值的最佳方法,非常适合需要同时获取异步操作结果来进行下一步运算的场合。
Promise.allSettled()
方法 Promise.allSettled(promises)
,返回一个在所有给定的 promise
都已经 fulfilled
或 rejected
后的 promise
,并带有一个对象数组,每个对象表示对应的promise
结果。
下面来看看 Promise.allSettled()
是如何工作的。
1.工作原理
Promise.allSettled()
可用于并行执行独立的异步操作,并收集这些异步操作的结果。
函数接受一个 promise
数组(或通常是一个可迭代的)作为参数,如下:
const statusesPromise = Promise.allSettled(promises);
当所有输入 promises
都被履行或拒绝时,statusesPromise
会解析为一个具有其状态的数组:
-
{ status: 'fulfilled', value:value }
: 如果相应的promise
已经履行 -
{ status: 'rejected', reason: reason }
:如果相应的promise
被拒绝
可以使用 then
方法提取所有 promises
的状态:
statusesPromise.then((statuses) => { statuses; // [{ status: '...', value: '...' }, ...] });
也可以使用 async/await
语法:
const statuses = await statusesPromise; statuses; // [{ status: '...', value: '...' }, ...]
Promise.allSettled()
返回的承诺总是以一系列状态实现,无论是否有一些(或者全部)输入承诺被拒绝。
Promise.allSettled()
和Promise.all()
的最大不同:Promise.allSettled()
永远不会被rejected
。
2. 使用指南
现在来深入介绍 Promise.allSettled()
的使用之前, 还是先来定义 2 个简单的函数。
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); } function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); }
接下来使用上面定义的2个辅助函数来试试 Promise.allSettled()
。
2.1 完成所有 promises
下面定义了一个 promise
数组 statusesPromise
,所有的 promise
都能够成功的 resolve
值,如下:
const fruits = ["potatoes", "tomatoes"]; const vegetables = ["oranges", "apples"]; const statusesPromise = Promise.allSettled([ resolveTimeout(fruits, 2000), resolveTimeout(vegetables, 1000), ]); // 等待 2 秒 ... const list = async () => { try { const statuses = await statusesPromise; console.log(statuses); } catch (error) { console.log(error); } }; list(); // [{ status: 'fulfilled', value: [ 'potatoes', 'tomatoes' ] },{ status: 'fulfilled', value: [ 'oranges', 'apples' ] }]
从上面执行的结果来看 Promise.allSettled()
返回的一个 promise
的 resolve
状态数组是按照执行前 statusesPromise
的顺序组成其结果。
2.2 一个 promise
被 rejected
将上面第一个 promise
出现异常被 rejected
,如下代码:
const fruits = ["potatoes", "tomatoes"]; const statusesPromise = Promise.allSettled([ resolveTimeout(fruits, 2000), rejectTimeout(new Error("Vegetables is empty"), 1000), ]); // 等待 2 秒 ... const list = async () => { try { const statuses = await statusesPromise; console.log(statuses); } catch (error) { console.log(error); } }; list(); // // [{ status: 'fulfilled', value: ['potatoes', 'tomatoes'] },{ status: 'rejected', reason: Error('Vegetables is empty') }]
即使输入数组中的第二个 promise
被 rejected
, statusesPromise
仍然可以成功解析状态数组。
2.3 所有 promises
被 rejected
将上面所有的 promises
出现异常被 rejected
,如下代码:
const statusesPromise = Promise.allSettled([ rejectTimeout(new Error("Fruits is empty"), 2000), rejectTimeout(new Error("Vegetables is empty"), 1000), ]); // 等待 2 秒 ... const list = async () => { try { const statuses = await statusesPromise; console.log(statuses); } catch (error) { console.log(error); } }; list(); // // [{ status: 'rejected', reason: Error('Fruits is empty') },{ status: 'rejected', reason: Error('Vegetables is empty') }]
小结
当需要执行并行和独立的异步操作并收集所有结果时,Promise.allSettled()
就是不错的选择,即使一些异步操作可能失败。
Promise.race()
方法 Promise.race(promises)
,顾名思义就是赛跑的意思,Promise.race([p1, p2, p3])
里面 promise
数组那个执行完成得快就获取那个的结果,不管结果本身是成功履行状态还是失败拒绝状态,只输出最快的 promise
。
下面来看看 Promise.race()
是如何工作的。
1.工作原理
Promise.race()
返回一个 promise
,一旦迭代器中的某个 promise
履行或拒绝,返回的 promise
就会履行或拒绝。
函数接受一个 promise
数组(或通常是一个可迭代的)作为参数,如下:
const racePromise = Promise.race(promises);
当所有输入 promises
中有一个 promise
快速被履行或拒绝时,racePromise
就会解析快速完成的 promise
结果(履行或拒绝):
可以使用 then
方法提取 racePromise
的结果:
racePromise.then((fastValue) => { fastValue // 快速完成的 promise });
也可以使用 async/await
语法:
const fastPromise = await racePromise; fastPromise; // 快速完成的 promise
Promise.race()
返回的承诺和最先完成的承诺信息一致。
Promise.race()
和Promise.any()
的不同:Promise.race()
承诺列表中寻找第一个履行或拒绝的承诺;Promise.any()
是从承诺列表中查找第一个履行的承诺。
2. 使用指南
现在来深入介绍 Promise.race()
的使用之前,同样先来定义 2 个简单的函数。
function resolveTimeout(value, delay) { return new Promise((resolve) => setTimeout(() => resolve(value), delay)); } function rejectTimeout(reason, delay) { return new Promise((r, reject) => setTimeout(() => reject(reason), delay)); }
接下来使用上面定义的2个辅助函数来试试 Promise.race()
。
2.1 完成所有 promises
下面定义了一个 promise
数组 racePromise
,所有的 promise
都能够成功的 resolve
值,如下:
const fruits = ["potatoes", "tomatoes"]; const vegetables = ["oranges", "apples"]; const racePromise = Promise.race([ resolveTimeout(fruits, 5000), resolveTimeout(vegetables, 1000), ]); // 等待 1 秒 ... const list = async () => { try { const fastPromise = await racePromise; console.log(fastPromise); } catch (error) { console.log(error); } }; list(); // [ 'oranges', 'apples' ]
从上面执行的结果来看 Promise.race()
返回最快履行的 promise
的 resolve
结果。
2.2 一个 promise
被 rejected
将上面第一个 promise
出现异常被 rejected
,如下代码:
const fruits = ["potatoes", "tomatoes"]; const racePromise = Promise.race([ resolveTimeout(fruits, 2000), rejectTimeout(new Error("Vegetables is empty"), 1000), ]); // 等待 1 秒 ... const list = async () => { try { const fastPromise = await racePromise; console.log(fastPromise); } catch (error) { console.log(error); } }; list(); // Error: Vegetables is empty
从上面的结果看,最先完成的 promise
被 rejected
,那么 fastPromise
返回的 promise
也是被 rejected
。
下面将rejected
的承诺时间延长到 5秒,如下:
const fruits = ["potatoes", "tomatoes"]; const racePromise = Promise.race([ resolveTimeout(fruits, 2000), rejectTimeout(new Error("Vegetables is empty"), 5000), ]); // 等待 2 秒 ... const list = async () => { try { const fastPromise = await racePromise; console.log(fastPromise); } catch (error) { console.log(error); } }; list(); // [ 'potatoes', 'tomatoes' ]
从上面执行结果看到,最快完成的 promise
履行了 resolve
,那么 fastPromise
返回的 promise
也是履行了 resolve
。
2.3 所有 promises
被 rejected
将上面所有的 promises
出现异常被 rejected
,如下代码:
const racePromise = Promise.race([ rejectTimeout(new Error("Fruits is empty"), 2000), rejectTimeout(new Error("Vegetables is empty"), 1000), ]); // 等待 1 秒 ... const list = async () => { try { const fastPromise = await racePromise; console.log(fastPromise); } catch (error) { console.log(error); } }; list(); // Error: Vegetables is empty
从结果来看,虽然两个承诺都被拒绝了,fastPromise
返回的 promise
是最快被拒绝的 。
3. 使用场景
3.1. 性能测试
在有异步操作的项目中,在对于网络或数据库请求进行性能进行优化的时候,可以使用 Promises
来测试其优化效果,通过使用 Promise.race()
来测试两种不同的方法的响应速度。
3.2 最佳选择
例如获取同一类型的数据有多个请求服务器,同时向多个服务器发送请求,只要其中一个完成工作,就将其数据呈现,达到选择最佳线路的效果。这是可以使用 Promise.race()
同时执行 promise
并在第一个成功后立即完成。
小结
Promise.race()
为第一个已解决和已拒绝的 promise
执行回调函数,而 Promise.any()
为第一个已履行的 promise
执行回调函数,如果没有履行的 promise
则拒绝一个特殊属性 AggregateError
。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Redis大集群扩容性能优化实践
一、背景 在现网环境,一些使用Redis集群的业务随着业务量的上涨,往往需要进行节点扩容操作。 之前有了解到运维同学对一些节点数比较大的Redis集群进行扩容操作后,业务侧反映集群性能下降,具体表现在访问时延增长明显。 某些业务对Redis集群访问时延比较敏感,例如现网环境对模型实时读取,或者一些业务依赖读取Redis集群的同步流程,会影响业务的实时流程时延。业务侧可能无法接受。 为了找到这个问题的根因,我们对某一次的Redis集群迁移操作后的集群性能下降问题进行排查。 1.1 问题描述 这一次具体的Redis集群问题的场景是:某一个Redis集群进行过扩容操作。业务侧使用Hiredis-vip进行Redis集群访问,进行MGET操作。 业务侧感知到访问Redis集群的时延变高。 1.2 现网环境说明 目前现网环境部署的Redis版本多数是3.x或者4.x版本; 业务访问Redis集群的客户端品类繁多,较多的使用Jedis。本次问题排查的业务使用客户端Hiredis-vip进行访问; Redis集群的节点数比较大,规模是100+; 集群之前存在扩容操作。 1.3 观察现象 因为时延变高...
- 下一篇
从网络通信的演进过程彻底搞懂Redis高性能通信的原理(全网最详细,建议收藏)
我们一直说Redis的性能很快,那为什么快?Redis为了达到性能最大化,做了哪些方面的优化呢? 在深度解析Redis的数据结构 这篇文章中,其实从数据结构上分析了Redis性能高的一方面原因。 在目前的k-v数据库的技术选型中,Redis几乎是首选的用来实现高性能缓存的方案,它的性能有多快呢? 根据官方的基准测试数据,一台普通硬件配置的Linux机器上运行单个Redis实例,处理简单命令(O(n)或者O(logn)),QPS可以达到8W,如果使用pipeline批处理功能,QPS最高可以达到10W。 Redis 为什么那么快 Redis的高性能主要依赖于几个方面。 C语言实现,C语言在一定程度上还是比Java语言性能要高一些,因为C语言不需要经过JVM进行翻译。 纯内存I/O,内存I/O比磁盘I/O性能更快 I/O多路复用,基于epoll的I/O多路复用技术,实现高吞吐网络I/O 单线程模型,单线程无法利用到多核CPU,但是在Redis中,性能瓶颈并不是在计算上,而是在I/O能力,所以单线程能够满足高并发的要求。 从另一个层面来说,单线程可以避免多线程的频繁上下文切换以及同步锁机制带...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Mario游戏-低调大师作品
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果