Promise
1、Promise 是一个类,类中需要传入一个 executor 执行器,默认会立即执行
2、Promise 有三种状态,pending
,resolved
,rejected
, 只能从等待态转为其他两种状态
3、promise 官网规范:https://promisesaplus.com/
4、promise 支持链式调用,内部原理采用的是发布订阅模式
5、每次调用 then 方法,都会返回一个新的 promise
6、.then 中什么时候走失败:返回的是一个失败的 promise,抛出异常也会走失败,其他都走成功
7、catch 用来统一捕获错误,它的特点是:如果一层层往下找,都没有处理错误的函数,则会找到最近的 catch,catch 也是 then, 遵循 then 的规则
8、Promise.all
如果有一个执行错误了,其他的还是会执行的,因为 promise 在实例化的时候就执行了
第一版:没有任何异步逻辑
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined
this.reason = undefined
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
try { //为什么加try..catch,因为内部可能直接出错
executor(resolve, reject) //给用户提供两个函数
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
if (this.status === RESOLVED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
}
}
module.exports = Promise
第二版:如果 executor 里面有异步操作,则需要采用发布订阅模式进行处理
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
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.value = value
this.status = RESOLVED
// 当调用resolve时,说明要走成功态,所以依次执行then中成功的回调
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
// 当调用reject时,说明要走失败态,所以执行then中失败的回调
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
if (this.status === RESOLVED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
//如果有异步操作(如setTimeout),执行then方法时状态还是PENDING,则需要将then中的两个方法先存起来
if (this.status === PENDING) {
//第一种:将这个函数直接扔进去,但是不太好,不方便扩展自己的功能
// this.onResolvedCallbacks.push(onfulfilled)
// this.onRejectedCallbacks.push(onrejected)
// 第二种:使用切片编程,外面包一层,方便扩展
this.onResolvedCallbacks.push(() => {
onfulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onrejected(this.reason)
})
}
}
}
module.exports = Promise
第三版:then 的链式调用及 then 是个异步操作
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
// 一般微任务比宏任务早执行,也不是绝对的,setTimeout是宏任务
// 判断x的状态 是让promise2走成功态还是失败态
function resolvePromise(promise2, x, resolve, reject) {
// TODO
}
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.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
// 链式调用,返回的需要是个新的promise
const promise2 = new Promise((resolve, reject) => {
// 将之前判断条件放到这个promise的里面,因为它是promise,所以是立即执行,不影响
if (this.status === RESOLVED) {
//将then执行之后的结果传到下一个promise的resolve或者reject中
// 需要判断这个x是普通值还是promise,如果是promise,则需要让这个promise执行,执行之后的状态将作为promise2的状态
setTimeout(() => { //为什么需要setTimeout?因为官方说了then是个异步操作
try { //为什么要包住,因为执行onfulfilled可能会出错
let x = onfulfilled(this.value)
// 我需要根据x,判断调用promise2的resolve还是reject,所以将promise2和它的resolve和reject传过去
// 此时应该没有promise2,应该怎么搞?答案就是通过添加宏任务(setTimeout)或者微任务(nextTick)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(x)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(x)
} catch (e) {
reject(e)
}
}, 0);
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(x)
} catch (e) {
reject(e)
}
}, 0);
})
}
})
return promise2
}
}
module.exports = Promise
第四版:resolvePromise 方法的实现
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {
// 此方法 为了兼容所有的promise,n个库中间 执行的流程是一样的
// 尽可能详细 不出错
// 1) 不能引用同一个对象 可能会造成死循环
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
}
let called;// 判断是否被调用过
// 2) 判断x的类型 x是对象或者函数,才有可能是promise
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then; // {a:1} 因为then方法 可能使用的getter来定义的
if (typeof then === 'function') { // 只能认为他是promise了
// call 改变this指向 并且让函数执行
then.call(x, y => { // 只取一次 当前promise解析出来的结果可能还是一个promise继续解析直到他是一个普通值为止
if (called) return;
called = true;
// 递归解析resolve的值
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return;
called = true;
reject(r);
})
} else {
// 如果不是函数,则只能是这种对象了{a:1,then:1}
resolve(x)
}
} catch (e) { // 我取then出错了 在错误中又掉了该promise的成功
if (called) return
called = true;
reject(e); //取值失败 就走到error中
}
} else {
// 进到这里说明是普通值,走成功态
resolve(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.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
catch (errCallback) { // catch就是没有成功的then方法
return this.then(null, errCallback)
}
then(onfulfilled, onrejected) {
const promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === REJECTED) {
setTimeout(() => {
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
}
}
module.exports = Promise
// npm install -g promises-aplus-tests
Promise的其他方法实现
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {
// 此方法 为了兼容所有的promise,n个库中间 执行的流程是一样的
// 尽可能详细 不出错
// 1) 不能引用同一个对象 可能会造成死循环
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
}
let called; // 判断是否被调用过
// 2) 判断x的类型 x是对象或者函数,才有可能是promise
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then; // {a:1} 因为then方法 可能使用的getter来定义的
if (typeof then === 'function') { // 只能认为他是promise了
// call 改变this指向 并且让函数执行
then.call(x, y => { // 只取一次 当前promise解析出来的结果可能还是一个promise继续解析直到他是一个普通值为止
if (called) return;
called = true;
// 递归解析resolve的值
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return;
called = true;
reject(r);
})
} else {
// 如果不是函数,则只能是这种对象了{a:1,then:1}
resolve(x)
}
} catch (e) { // 我取then出错了 在错误中又掉了该promise的成功
if (called) return
called = true;
reject(e); //取值失败 就走到error中
}
} else {
// 进到这里说明是普通值,走成功态
resolve(x)
}
}
class Promise {
static resolve(param) {
if (param instanceof Promise) return param;
return new Promise((resolve, reject) => {
// 如果param是个带then的对象,并且这个then还是个函数走这里
// var obj = {
// then(resolve,reject){
// resolve(25)
// }
// }
if (param && param.then && typeof param.then === 'function') {
param.then(resolve, reject)
} else {
resolve(param)
}
})
}
static reject(reson) {
return new Promise((resolve, reject) => {
reject(reson)
})
}
static all(promiseArr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArr)) {
return reject('传入的参数必须是数组!')
}
let arr = []
let count = 0 //采用计数器的方式
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then(res => { //不是promise的也给你转成promise
count++
arr[i] = res //保证返回的结果顺序
if (count === promiseArr.length) {
resolve(arr)
}
})
}
})
}
static race(promiseArr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArr)) {
return reject('传入的参数必须是数组!')
}
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then(res => { //不是promise的也给你转成promise
console.log('cc');
resolve(res)
return
}, err => {
console.log('dd');
reject(err)
return
})
}
})
}
constructor(executor) {
this.status = PENDING;
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
catch (errCallback) { // catch就是没有成功的then方法
return this.then(null, errCallback)
}
finally(cb) {
return this.then(value => {
return Promise.resolve(cb()).then(() => {
return value
})
}, err => {
return Promise.resolve(cb()).then(() => {
throw err
})
})
}
then(onfulfilled, onrejected) {
const promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === REJECTED) {
setTimeout(() => {
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
}
}
module.exports = Promise
常见面试题
promise
链式调用原理和jquery
链式调用原理区别
答:jquery中的链式调用原理是通过返回this实现的,而promise的链式调用是通过返回新的promise实现的
Promise.all
方法的原理
答:all方法的特点是让所有的promise并发执行,内部使用**计数器**的方式判断是否全部执行完了,内部使用递归
Promise.all
如何实现?第一版(考虑全是promise的情况)
function PromiseAll(promiseArr) { // your code } const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(1) }, 1000); }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve(2) }, 2000); }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(3) }, 3000); }) PromiseAll([p1, p2, p3]).then(res => { console.log(res) //3 秒之后打印 [1,2,3] }).catch(e => { console.log(e) })
function PromiseAll(promiseArr) { return new Promise((resolve, reject) => { if (!Array.isArray(promiseArr)) { return reject('传入的参数必须是数组!') } let arr = [] for (let i = 0; i < promiseArr.length; i++) { promiseArr[i].then(res => { arr.push(res) if (arr.length === promiseArr.length) { resolve(arr) } }) } }) }
缺点:
promise.all
里面的并不一定全是promise,还可以是其他类型- 如果我们换成
PromiseAll([p1, p3, p2])
这种方式,并不会按照我们调用的顺序输出[1,3,2]
, 而是[1,2,3]
, 不符合预期
- 第二版(考虑不全是promise的情况和输出顺序问题)
function PromiseAll2(promiseArr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArr)) {
return reject('传入的参数必须是数组!')
}
let arr = []
let count = 0 //采用计数器的方式
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then(res => { //不是promise的也给你转成promise
count++
arr[i] = res //保证返回的结果顺序
if (count === promiseArr.length) {
resolve(arr)
}
})
}
})
}
- Promise.resolve 和 Promise.reject 区别
Promise.resolve() 参数可以接受一个promise, 并且等待它状态改变;
Promise.reject() 也可以接受promise, 但是不会有等待效果, 直接会走失败态
- promise.finally() 的实现原理
finally 相当于 then, 内部也是调用了 then
如果 finally 返回的是 promise2,则会等待这个 promise2 执行完毕,如果 promise2 成功,则会采用上个 promise 的结果(上个 promise 成功就走成功,失败就走失败),如果失败,则会采用 promise2 的失败原因走失败态
Function.prototype.finally = function(cb) {
return this.then(value => {
return Promise.resolve(cb()).then(() => value)
}, reson => {
return Promise.resolve(cb()).then(() => {
throw reson
})
})
}
- 面试题 1
// 需要知道promise.then()是个微任务,而new Promise()或者Promise.resolve()会立即执行
console.log(1)
async function async () {
console.log(2)
await console.log(3) //console.log(3)这里相当于Promise.resolve(console.log(3))
console.log(4) //console.log(4)相当于Promise.resolve(console.log(3)).then(res=>console.log(4))
}
setTimeout(() => {
console.log(5)
}, 0);
const promise = new Promise((resolve, reject) => {
console.log(6)
resolve(7)
})
promise.then(res => {
console.log(res)
})
async ()
console.log(8)
// 1
// 6
// 2
// 3
// 8
// 7
// 4
// 5
- 第二道(头条面试题)
浏览器识别 async+await,如果 await 后面跟的是 promise 的话默认直接调用该 promise 的 then 方法
node 环境中并不能识别 await 后面的,所以它会包一层 promise
async function async1() {
console.log(1)
// 浏览器环境中
// async2().then(()=>{
// console.log(2)
// })
await async2()
console.log(2)
// node环境中
// new Promise((resolve,reject)=>resolve(async2())).then(()=>{
// console.log(2)
//})
}
async function async2() {
console.log(3)
}
console.log(4)
setTimeout(() => {
console.log(5)
}, 0);
async1()
new Promise((resovle, reject) => {
console.log(6)
resovle()
}).then(res => {
console.log(7)
})
console.log(8)
// 4
// 1
// 3
// 6
// 8
// 2
// 7
// 5
- 第三道
知识点:promise 只有当执行resolve
时才会将then
里面的回调放到微任务队列中
console.log(1)
setTimeout(() => {
console.log(2)
Promise.resolve().then(() => {
console.log(3)
})
}, 0);
new Promise((resolve, reject) => {
console.log(4)
setTimeout(() => {
console.log(5)
resolve(6)
}, 0);
}).then(res => {
console.log(7)
setTimeout(() => {
console.log(res)
}, 0)
})
// 1
// 4
// 第一轮宏任务结束,此时清空微任务,发现没有微任务
// 第二轮宏任务开始
// 2
// 第二轮宏任务结束,此时有微任务,要清空微任务
// 3
// 第三论宏任务开始,
// 5 resolve(6)
// 宏任务结束,执行微任务
// 7
// 6
- 第四道
const p = function() {
return new Promise((resolve, reject) => {
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 0);
resolve(2)
})
p1.then(res => {
console.log(res)
})
console.log(3)
resolve(4)
})
}
p().then(res => {
console.log(res)
})
console.log('end')
// 3
// end
// 2
// 4
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。