1.事件环-宏任务和微任务
说到promise,还是得理清楚事件环中宏任务和微任务的关系。我们都知道js代码块是单进程的,代码执行顺序从上往下执行,其中的异步任务会添加到事件队列中,异步任务又分为宏任务和微任务ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
1.2 常见的宏任务和微任务
宏任务:setTimeout,setInterval,script,MessageChannel,I/O,事件队列 微任务:Promise,process.nextTick(Node环境)
1.2 我所理解的事件环-Eventloop
本人对于事件环一开始也是懵懵懂懂,半知半解的,不搞懂吧,有总感觉心里刺挠,于是我拿着纸笔一顿乱写乱画,在那一瞬间似乎有种东西抑制不住的要喷涌而出,片刻后,爽~!于是就得到了下面这张图,朋友们,请看下图。
我们知道,js在执行完宏任务后(script),会去清空微任务队列。在script代码块执行的过程中,遇到宏任务就添加到宏任务的队列中,遇到微任务就添加到微任务的队列中。单单从这句话中,我个人还是难以理清任务间代码执行顺序的问题,既然如此,不妨大胆点,将宏任务和微任务加上等级标识,宏任务和微任务之间执行顺序的权重关系。上代码
<script>
setTimeout(() => {
console.log('setTimeout')
Promise.resolve().then(() => {
console.log('Promise2')
})
}, 0)
Promise.resolve().then(() => {
console.log('Promise1')
}).then(res => {
console.log('Promise1.2')
})
console.log('script')
</script>
我们将script代码块内的代码看成第一级宏任务队列,script代码执块行过程中,分为以下两步
- 执行宏任务队列中宏任务时,遇到宏任务添加到下一级宏任务队列中,遇到微任务添加到同级微任务队列中,当前宏任务执行完成后,如果异步队列中存在微任务,执行第二步,清空微任务队列,否之继续执行第一步,直至异步队列清空
- 在清空微任务队列时,遇到宏任务添加至下级宏任务队列,遇到微任务时,添加到同级微任务队列(当前微任务队列),清空微任务队列后,如果异步队列中存在宏任务,执行第一步
我们把上图中的每个方块看成了一个任务队列,实际上每次队列执行完后都会清空,为了形象的理解,增加了等级的概念,实际上还是同一个任务队列。
重点:每次执行完弘任务都要清空微任务队列。 总结:去掉等级概念后,代码在执行弘任务时,遇到弘任务添加到弘任务队列中,遇到微任务添加到微任务队列中,当前弘任务执行完成时,清空微任务队列,之行微任务时,遇到弘任务添加到弘任务队列中,遇到微任务添加到微任务队列中,微任务队列清空时,执行弘任务队列中的下一个弘任务,开始下一次循环。 精简:代码在执行异步任务时,遇到弘任务添加到弘任务队列中,遇到微任务添加到微任务队列中,每次执行完弘任务都要清空微任务队列,再执行下一个弘任务,开始下一次循环。
<script>
setTimeout(() => {
console.log('setTimeout')
Promise.resolve().then(() => {
console.log('Promise3')
})
}, 0)
Promise.resolve().then(() => {
console.log('Promise1')
setTimeout(() => {
console.log('setTimeout2')
}, 0)
}).then(() => {
console.log('Promise2')
})
console.log('script')
</script>
上面这道题想必大家一看就知道正确答案了吧,接下来我们要步入今天的正题了——promise
2. promise分析
2.1 promise要点归集
- Promise 是一个类,在执行这个类的时候会传入一个执行器,这个执行器会立即执行
- Promise 会有三种状态
- Pending 等待
- Fulfilled 完成
- Rejected 失败
- 状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改;
- Promise 中使用 resolve 和 reject 两个函数来更改状态;then 方法内部做但事情就是状态判断,如果状态是成功,调用成功回调函数,如果状态是失败,调用失败回调函数
- value-成功后向下传递的值,reason失败后向下传递的值
- promise状态未改变时,收集then上成功和失败回调
- then方法支持链式调用
2.2 promise类的实现
依照上面4点就可以新建一个promise类了
// 状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor){
// executor 是一个执行器,进入会立即执行-即promise执行器中的代码为同步代码
// 并传入resolve和reject方法
executor(this.resolve, this.reject)
}
// 储存状态的变量,初始值是 pending
status = PENDING
// 成功之后向下传递的值
value = null;
// 失败之后的原因
reason = null;
// 存储成功回调
onFulfilledCallback = [];
// 存储失败回调
onRejectedCallback = [];
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// 清空存储的成功回调
this.onFulfilledCallback.forEach(onFulfilled => onFulfilled(this.value))
this.onFulfilledCallback = []
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 清空存储的失败回调
this.onRejectedCallback.forEach(onRejected => onRejected(this.reason))
this.onRejectedCallback = []
}
}
// then方法
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
} else {
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
}
}
}
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000);
})
promise.then(value => {
console.log('resolve', value)
}, reason => {
console.log('reject', reason)
})
2.3 then方法链式调用
- 链式调用即返回一个promise对象
- 通过return向下一个then传递参数
then(onFulfilled, onRejected) {
// ==== 新增 ====
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const thenPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(x, resolve, reject);
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
})
return thenPromise;
}
// 判断x是不是 MyPromise 实例对象
function resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
避免循环引用,需要对then 方法返回的是自己的 Promise 对象的时候做处理。
resolvePromise(thenPromise, x, resolve, reject);
function resolvePromise(thenPromise, x, resolve, reject) {
// 如果return的是自己,抛出类型错误并返回
if(x === thenPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
这个时候then中代码的执行,必须依赖(thenPromise完成初始化,我们使用queueMicrotask创建一个异步函数去等待 promise2 完成初始化
then(onFulfilled, onRejected) {
...
if (this.status === FULFILLED) {
queueMicrotask(() => {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
}
...
}
catch方法
catch方法可以看成then的语法糖
catch(onRejected) {
this.then(null, onRejected)
}
改变then的传参,使其变得不必须,并且,如果then没有返回值,上次的结果将透传
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
...
}
添加resolve、reject返回promise对象
MyPromise {
......
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
return value instanceof Promise ? value : new Promise((resolve) => resolve(value));
}
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}
全部代码
class MyPromise {
constructor(executor){
// executor 是一个执行器,进入会立即执行-即promise执行器中的代码为同步代码
// 并传入resolve和reject方法
executor(this.resolve, this.reject)
}
// 储存状态的变量,初始值是 pending
status = PENDING
// 成功之后向下传递的值
value = null;
// 失败之后的原因
reason = null;
// 存储成功回调
onFulfilledCallback = [];
// 存储失败回调
onRejectedCallback = [];
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// 清空存储的成功回调
this.onFulfilledCallback.forEach(onFulfilled => onFulfilled(this.value))
this.onFulfilledCallback = []
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 清空存储的失败回调
this.onRejectedCallback.forEach(onRejected => onRejected(this.reason))
this.onRejectedCallback = []
}
}
// then方法
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
const thenPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(thenPromise, x, resolve, reject);
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
})
return thenPromise;
}
catch(onRejected) {
this.then(null, onRejected)
}
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
return value instanceof Promise ? value : new Promise((resolve) => resolve(value));
}
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}
function resolvePromise(thenPromise, x, resolve, reject) {
// 如果return的是自己,抛出类型错误并返回
if(x === thenPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
promise A+
finally
无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例
finally(callback) {
this.then(callback, callback)
}
race
无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例
race(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach((promise) => {
MyPromise.resolve(promise).then(resolve, reject)
})
})
}
all
利用与入参数组长度相同的结果数组以索引映射方式去接收结果集,没成功接收一个结果,计数器+1,当达到入参数组长度时,表示所有异步操作都成功,返回结果
all(promiseArr) {
return new MyPromise((resolve, reject) => {
// 如果Promise.all接收到的是一个空数组([]),它会立即返回结果。
if (!promiseArr.length) {
resolve([])
}
length = promiseArr.length
let result = new Array(length).fill(null)
let resolvedPro = 0
for (let index = 0; index < length; index++) {
MyPromise.resolve(promiseArr[index]).then(
(data) => {
// 注意,这里要用index赋值,而不是push。因为要保持返回值和接收到的promise的位置一致性。
result[index] = data
if (++resolvedPro === length) {
resolve(result)
}
},
(error) => {
reject(error)
}
)
}
})
}
allSettled
与all同理,只是失败的时候依旧执行resolve向下传递
allSettled(promiseArr) {
return new MyPromise((resolve, reject) => {
if (!promises.length) {
resolve([]);
}
length = promiseArr.length
let result = new Array(length).fill(null)
let resolvedPro = 0
for (let index = 0; index < length; index++) {
myPromise.resolve(promiseArr[index])
.then((data) => {
// 注意,这里要用index赋值,而不是push。因为要保持返回值和接收到的promise的位置一致性。
result[index] = {
status: FULFILLED_STATE,
value: data,
};
if (++resolvedPro === length) {
resolve(result);
}
})
.catch((error) => {
result[index] = {
status: REJECTED_STATE,
reason: error,
};
if (++resolvedPro === length) {
resolve(result)
}
})
}
})
}