

手写系列-这一次,彻底搞懂 Promise
source link: https://www.iyouhun.com/post-258.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

手写系列-这一次,彻底搞懂 Promise

想要实现 Promise,必须先了解 Promise 是什么,以及 Promise 有哪些功能。
还不是特别了解 Promise 的同学,建议先移步 ES6入门-Promise 熟悉。
Promise 是基于 Promises/A+ 规范 实现的,换句话说,我们可以按照 Promises/A+ 规范 来手写 Promise。
1.1 小例子
Promise,直译过来就是承诺,Promise 到底承诺了什么呢?
当我在麦当劳点一份汉堡套餐,收银员会给我一张收据,这个收据就是 Promise,代表我已经付过钱了,麦当劳会为我做一个汉堡套餐的承诺,我要通过收据来取这个汉堡套餐。
那么这个买汉堡得到的承诺会有以下 3 种状态:
- 等待状态:我刚下单,汉堡还没做好,这时我可以在等待汉堡时,同时做其他事情;
- 成功状态:汉堡做好了,通知我取餐;
- 失败状态:发现卖完了,通知我退款;
需要注意的是,状态的修改是不可逆的,当汉堡做好了,承诺兑现了,就不能再回到等待状态了。
总结一下,Promise 就是一个承诺,承诺会给你一个处理结果,可能是成功的,可能是失败的,而返回结果之前,你可以同时做其他事情。
二、Promises/A+
接下来,按照 Promises/A+ 规范 一步步实现 Promise。
1. Promise 基本用法
先瞅一眼 ES6 Promise 基本用法。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value)
} else {
reject(error)
}
});
promise.then(data => {
console.log('请求成功')
}, err => {
console.log('请求失败')
})
1.1 Promise 状态
Promise 拥有自己的状态,初始状态->成功状态时,执行成功回调,初始状态->失败状态时,执行失败回调。
- pending:初始状态,可以转换为
fulfilled
或rejected
状态; - fulfilled:成功状态,转换到该状态时必须有成功返回值,且不能再次转换状态;
- rejected:失败状态,转换到该状态时必须有错误原因,且不能再次转换状态;
通过已知的 Promise 3 种状态,可定义常量 STATUS 和 MyPromise 状态 status。
代码如下:
// Promise 3 种状态
const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
// 初始状态为 pending
status = STATUS.PENDING
}
1.2 执行器
从基本用法可知,Promise 需要接收 1 个执行器函数作为参数,这个函数带有 2 个参数。
- resolve:把 Promise 状态改成成功;
- reject:把 Promise 状态改成失败;
代码如下:
class MyPromise {
constructor (executor) {
// 执行器
executor(this.resolve, this.reject)
}
// 成功返回值
value = null
// 失败返回值
reason = null
// 修改 Promise 状态,并定义成功返回值
resolve = value => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED
this.value = value
}
}
// 修改 Promise 状态,并定义失败返回值
reject = reason => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED
this.reason = reason
}
}
}
}
1.3 then
Promise 拥有 then
方法,then 方法第一个参数是成功状态的回调函数 onFulfilled
,第二个参数是失败状态的回调函数 onRejected
。
promise.then(onFulfilled, onRejected)
onFulfilled 要求如下:
- 必须在 promise 状态为完成时调用它,并将 promise 的 value 作为它的第一个参数;
- 在 promise 完成之前不能调用它;
- 它不能被多次调用;
onRejected 要求如下:
- 必须在 promise 被拒绝后调用它,以 promise.reason 作为它的第一个参数;
- 在 promise 被拒绝之前不能调用它;
- 它不能被多次调用;
代码如下:
class MyPromise {
then = function (onFulfilled, onRejected) {
if (this.status === STATUS.FULFILLED) {
onFulfilled(this.value)
} else if (this.status === STATUS.REJECTED) {
onRejected(this.reason)
}
}
}
1.4 试试看
按照 Promise 的基本用法,创建 MyPromise 实例 mypromise。
const mypromise = new MyPromise((resolve, reject) => {
resolve('成功')
})
mypromise.then(data => {
console.log(data, '请求成功') // 成功打印“成功 请求成功”
}, err => {
console.log(err, '请求失败')
})
2. Promise.then
下文将按照 Promises/A+ 规范 完善 MyPromise.then 方法。
Promises/A+ 规范 中标明 then 有以下要求:
1. 可选参数
onFulfilled、onRejected 是可选参数。
- 如果 onFulfilled 不是函数,则必须忽略它;
- 如果 onRejected 不是函数,则必须忽略它;
代码如下:
class MyPromise {
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
}
}
2. 多次调用 then
then 可以在同一个承诺上多次调用。
- 当 promise 完成,所有相应的 onFulfilled 回调必须按照它们的原始调用的顺序执行 then;
- 当 promise 被拒绝,所有相应的 onRejected 回调必须按照它们对 的原始调用的顺序执行 then;
2.1 数组缓存回调
可以理解为将 onFulfilled、onRejected 作为数组存储在 MyPromise 中,然后按照顺序执行。
代码如下:
class MyPromise {
// 成功回调
onFulfilledCallback = []
// 失败回调
onRejectedCallback = []
// 修改 Promise 状态,并定义成功返回值
resolve = value => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED
this.value = value
while(this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
// 修改 Promise 状态,并定义失败返回值
reject = reason => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED
this.reason = reason
while(this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.status === STATUS.PENDING) {
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
} else if (this.status === STATUS.FULFILLED) {
onFulfilled(this.value)
} else if (this.status === STATUS.REJECTED) {
onRejected(this.reason)
}
}
}
由此,我们已实现了一个基础的 Promise。
2.2 试试看
看了这么久,试一试 MyPromise 是否符合要求吧。
代码如下:
const mypromise = new MyPromise((resolve, reject) => {
resolve('成功')
})
mypromise.then(data => {
console.log(data, '1')
})
mypromise.then(data => {
console.log(data, '2')
})
输出结果如图:

由图可知,和预期一样。
3. 链式调用 then
then 必须返回一个 Promise 来支持链式调用 Promise。
示例代码如下:
mypromise.then(data => {
console.log(data, '请求成功')
return '2'
}).then(data => {
console.log(data, '请求成功')
return '3'
})
3.1 改写 then 方法
改动点如下:
- then 方法需要返回 MyPromise 实例;
- then 内部调用回调时,需通过 resolvePromise 方法判断返回值 x 的类型来处理返回值。
class MyPromise {
then = function (onFulfilled, onRejected) {
// 返回 MyPromise实例
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === STATUS.PENDING) {
this.onFulfilledCallback.push(() => {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
})
this.onRejectedCallback.push(() => {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
})
} else if (this.status === STATUS.FULFILLED) {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} else if (this.status === STATUS.REJECTED) {
const x = onRejected(this.error)
resolvePromise(promise2, x, resolve, reject)
}
})
return promise2
}
}
上述代码引用了 resolvePromise 来处理 Promise.then 的返回值
3.1.1 注意
这里的promise2暂时还不能正常运行,可能会报错:Cannot access 'promise2' before initialization
| 不能访问 promise2 在初始化之前。
原因:在 new promise 时,promise2 还没有完成初始化。所以 resolvePromise 中不能访问到 promise2,在当前的执行上下文栈中,onFulfilled 或 onRejected 是不能被直接调用的,onFulfilled 或 onRejected 得是在当前事件循环后异步执行的。
解决方法:可以使用 setTimeout、setImmediate、MutationObserever、process.nextTick在 then 方法被调用后将创建一个新的栈,这个我们后续处理,先正常往下看。
3.2 resolvePromise
Promises/A+ 规范 对resolvePromise 的要求如下:

- 如果 promise2 === x, 执行 reject,错误原因为 TypeError
- 如果 x 是函数或对象
- 如果 x.then 是函数
- 执行 x.then
- 如果 x.then 不是函数
- 执行 resolve(x)
- 如果 x.then 是函数
- 如果 x 不是函数或对象
- 执行 resolve(x)
代码如下:
function resolvePromise (promise2, x, resolve, reject) {
// 如果 promise2 === x, 执行 reject,错误原因为 TypeError
if (promise2 === x) {
reject(new TypeError('The promise and the return value are the same'))
}
// 如果 x 是函数或对象
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
let then
try {
then = x.then
} catch (error) {
reject(error)
}
// 如果 x.then 是函数
if (typeof then === 'function') {
// Promise/A+ 2.3.3.3.3 只能调用一次
let called = false
try {
then.call(x, y => {
// resolve的结果依旧是promise 那就继续解析 | 递归解析
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} catch (error) {
if (called) return
reject(error)
}
} else {
// 如果 x.then 不是函数
resolve(x)
}
} else {
// 如果 x 不是 promise 实例
resolve(x)
}
}
3.3 试一试
试试看能不能符合预期,链式调用 then 吧。
输出结果为:

成功符合预期!
4. 异步事件
Promises/A+ 规范 要求 onFulfilled、onRejected 在执行上下文堆栈之前不得调用。也就是3.1.1标明要注意的点。
4.1 事件队列
当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为事件队列。
当所有同步任务执行完成后,系统才会读取”事件队列”。
事件队列中的事件分为宏任务和微任务:
- 宏任务:浏览器/Node发起的任务,如 window.setTimeout;
- 微任务:Js 自身发起的,如 Promise;
事件队列就是先执行微任务,再执行宏任务,而宏任务和微任务包含以下事件:
宏任务 | 微任务 |
---|---|
setTimeout | Promise |
setInterval | queueMicrotask |
script(整体代码块) | - |
看看下面这个例子,你知道答案吗?
setTimeout(function () {
console.log(1)
})
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val)
})
console.log(4)
打印结果的顺序是2->4->3->1。事件队列如下:
主队列,同步任务,new Promise 内部的同步任务
new Promise(function(resolve,reject){ console.log(2) })
主队列,同步任务,new Promise 后的 console.log(4)
console.log(4)
异步任务的微任务
promise.then(function(val){ console.log(val) })
异步任务的宏任务
setTimeout(function () { console.log(1) })
因此,想要实现 onFulfilled、onRejected 在执行上下文堆栈之前不得调用,我们需要把 onFulfilled、onRejected 改造成微任务,这里使用 queueMicrotask 来模拟实现微任务,代码如下:
class MyPromise {
then (onFulfilled, onRejected) {
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
const realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = realOnRejected(this.reason)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === STATUS.PENDING) {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
} else if (this.status === STATUS.FULFILLED) {
fulfilledMicrotask()
} else if (this.status === STATUS.REJECTED) {
rejectedMicrotask()
}
})
return promise2
}
}
4.2 试试看
打印结果如图:

成功按顺序打印。
3. Promise/A+ 测试
下面将用 Promise/A+ 测试工具 promises-aplus-tests 测试我们手写的 Promise 是否符合规范。
3.1 安装 promises-aplus-tests
npm install promises-aplus-tests -D
3.2 为 MyPromise 添加 deferred
MyPromise {
......
}
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
});
return result;
}
module.exports = MyPromise
3.3 配置启动命令
"scripts": {
"test:promise": "promises-aplus-tests ./Promise/index"
}
3.4 开始测试
npm run test:promise
哇哦,全部成功!!

3.5 完整代码
截止到此,完整代码如下:
const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
constructor (executor) {
// 执行器
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
// 初始状态为 pending
status = STATUS.PENDING
// 成功返回值
value = null
// 失败返回值
reason = null
// 成功回调
onFulfilledCallbacks = []
// 失败回调
onRejectedCallbacks = []
// 修改 Promise 状态,并定义成功返回值
resolve = value => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED
this.value = value
// this.onFulfilledCallbacks.forEach(fn => fn())
while(this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 修改 Promise 状态,并定义失败返回值
reject = reason => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED
this.reason = reason
// this.onRejectedCallbacks.forEach(fn => fn())
while(this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason)
}
}
}
then = function (onFulfilled, onRejected) {
// onFulfilled、onRejected 是可选参数 不是函数则必须忽略它
const realOnFulfilled = onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
const realOnRejected = onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
const promise2 = new MyPromise((resolve, reject) => {
// 报错:Cannot access 'promise2' before initialization | 不能访问 promise2 在初始化之前
// 原因:在 new promise 时,promise2 还没有完成初始化,所以 resolvePromise 中不能访问到 promise2
// 在当前的执行上下文栈中,onFulfilled 或 onRejected 是不能被直接调用的
// onFulfilled 或 onRejected 得是在当前事件循环后异步执行的
// 可以使用 setTimeout、setImmediate、MutationObserever、process.nextTick在 then 方法被调用后将创建一个新的栈
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnRejected(this.reason)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === STATUS.PENDING) {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
} else if (this.status === STATUS.FULFILLED) {
fulfilledMicrotask()
} else if (this.status === STATUS.REJECTED) {
rejectedMicrotask()
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 如果 promise2 === x 执行 reject,错误原因为 TypeError
if (promise2 === x) {
reject(new TypeError('The promise and the return value are the same'))
}
// 如果 x 是函数或对象
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
let then
try {
then = x.then
} catch (error) {
reject(error)
}
// 如果 x.then 是函数
if (typeof then === 'function') {
// Promise/A+ 2.3.3.3.3 只能调用一次
let called = false
try {
then.call(x, y => {
// resolve的结果依旧是promise 那就继续解析 | 递归解析
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} catch (error) {
if (called) return
reject(error)
}
} else {
// 如果 x.then 不是函数
resolve(x)
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x)
}
}
// 测试 Promise 是否符合规范
MyPromise.deferred = function () {
var result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
下面的完善Promise 的 API将基于此基础代码。
4. Promise 的 API
虽然上述的 promise 源码已经符合 Promise/A+ 的规范,但是原生的 Promise 还提供了一些其他方法,如:
- Promise.resolve()
- Promise.reject()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
下面具体说一下每个方法的实现:
4.1 Promise.prototype.catch
Promise.prototype.catch 用来捕获 promise 的异常,就相当于一个没有成功的 then。
至于为什么先实现此方法,是为了防止实现其他 api 时会报错。
// ...
catch(errCallback) {
return this.then(null,errCallback)
}
// ...
4.2 Promise.prototype.finally
finally
表示不是最终的意思,而是无论如何都会执行的意思。 如果返回一个 promise
会等待这个 promise
也执行完毕。如果返回的是成功的 promise
,会采用上一次的结果;如果返回的是失败的 promise
,会用这个失败的结果,传到 catch
中。
finally(callback) {
return this.then((value)=>{
return MyPromise.resolve(callback()).then(()=>value)
},(reason)=>{
return MyPromise.resolve(callback()).then(()=>{throw reason})
})
}
MyPromise.resolve(456).finally(()=>{
return new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(123)
}, 3000)
})
}).then(data=>{
console.log(data,'success')
}).catch(err=>{
console.log(err,'error')
})
控制台等待 3s
后输出:456 'success'
,相反如果把resolve(123)
改为reject(123)
等待 3s
后输出123 'error'

4.3 Promise.resolve
默认产生一个成功的 promise。
static resolve(data){
return new MyPromise((resolve,reject)=>{
resolve(data)
})
}
这里需要注意的是,promise.resolve 是具备等待功能的。如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在原来 resolve
方法中做一个小小的处理:
// 修改 Promise 状态,并定义成功返回值
resolve = value => {
if(value instanceof MyPromise){
// 递归解析
return value.then(this.resolve,this.reject)
}
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED
this.value = value
while(this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(value)
}
}
}
4.4 Promise.reject
static reject(reason){
return new MyPromise((resolve,reject)=>{
reject(reason)
})
}
4.5 Promise.all
Promise.all
是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。
Promise.all
方法可以接收一个promise数组作为参数,返回一个新的promise对象,该对象在数组中所有promise都成功时才会被resolve。如果其中有一个promise失败,则Promise.all
会立即将其reject,并且不再等待其他promise的执行结果。
注意:这个参数数组里面也不是必须都是promise,也可以是常量普通值。
static all(values) {
if (!Array.isArray(values)) {
const type = typeof values
return new TypeError(`TypeError: ${type} ${values} is not iterable`)
}
return new MyPromise((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)
}
}
})
}
测试一下:
let p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('ok1')
}, 1000)
})
let p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('ok2')
}, 1000)
})
MyPromise.all([1,2,3,p1,p2]).then(data => {
console.log('resolve', data)
}, err => {
console.log('reject', err)
})
控制台等待 1s
后输出:resolve (5) [1, 2, 3, 'ok1', 'ok2']
4.6 Promise.race
Promise.race
用来处理多个请求,采用最快的(谁先完成用谁的)。
static race(promises) {
return new MyPromise((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 是没有中断方法的,xhr.abort()、ajax 有自己的中断方法,axios 是基于 ajax 实现的;fetch 基于 promise,所以他的请求是无法中断的。
这也是 promise 存在的缺陷,我们可以使用 race 来自己封装中断方法:
function wrap(promise) {
// 在这里包装一个 promise,可以控制原来的promise是成功还是失败
let abort
let newPromise = new MyPromise((resolve, reject) => { // defer 方法
abort = reject
});
let p = MyPromise.race([promise, newPromise]) // 任何一个先成功或者失败 就可以获取到结果
p.abort = abort
return p
}
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => { // 模拟的接口调用 ajax 肯定有超时设置
resolve('成功')
}, 1000);
});
let newPromise = wrap(promise)
setTimeout(() => {
// 超过3秒 就算超时 应该让 proimise 走到失败态
newPromise.abort('超时了')
}, 3000)
newPromise.then((data => {
console.log('成功的结果' + data)
})).catch(e => {
console.log('失败的结果' + e)
})
控制台等待 1s
后输出:成功的结果成功
以上,我们实现了一个符合 Promises/A+ 规范 的 Promise,也实现了Promise
一些常用API方法。
总结一下 Promise
其实就是一个帮助我们执行异步任务的对象,因为 Javascript 单线程的特性,导致必须通过为异步任务添加回调来得到异步任务的结果。为了解决回调地狱,Promise 应运而生。
Promise
通过对异步任务执行状态的处理,让我们可以在 Promise.then
中获取任务结果,让代码更加清晰优雅。
Promise.then
的链式调用,以顺序的方式来表达异步流,让我们更好的维护异步代码。
注:该文基于 https://juejin.cn/post/6973155726302642206 基础上转载修改完善,感谢。
Recommend
-
63
使用 Git 已经好几年了,却始终只是熟悉一些常用的操作。对于 Git Rebase 却很少用到,直到这一次,不得不用。 一、起因 上线构建的过程中扫了一眼代码变更,突然发现, commit 提交竟然多...
-
38
mybatis的前身是iBatis,其源于“Internet”和“abatis”的组合,是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。 mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集,它可以使用简单的XML...
-
23
etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册于发现,还可以作为 key-value 存储的中间件。 《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口...
-
25
etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件。 《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口、...
-
22
前言 在使用Spring和SpringMVC的老版本进行开发时,我们需要配置很多的xml文件,非常的繁琐,总是让用户自行选择配置也是非常不好的。基于 约定大于配置 的规定,Spring提供了很多注解帮助我们简化了大量的x...
-
35
etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件。 《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口...
-
23
etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件。 《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口、...
-
4
手写Promise/Promise.all/Promise.race(手写系列一)饥人谷若愚饥人谷前端,培养有灵魂的前端工程师
-
6
多线程并发是Java语言中非常重要的一块内容,同时,也是Java基础的一个难点。说它重要是因为多线程是日常开发中频繁用到的知识,说它难是因为多线程并发涉及到的知识点非常之多,想要完全掌握Java的并发相关知识并非易事。也正因此,Java并发成了Java面试中最高...
-
6
手写一个Redis分布式锁,让你彻底搞懂 作者:指北君 2022-11-11 08:19:03 如果程序运行的极慢(硬件处理慢或者进行了GC),导致30秒已经到了,锁已经失效了,程序还没有运行完成,这时候,就会有另一个线程总想钻...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK