本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠))
喜欢的话点个赞,谢谢!
1. Promise 介绍
在实际开发中Promise函数是必不可少的一环,我们经常用来对异步函数进行同步编程范式的处理,比如请求超时、异步获取chrome.storage存储等,JS基础面试时也经常会问到,不熟悉Promise的话基本上gg了((✿◠‿◠))
Promise实际上是一个对象,是用来表示一个异步任务结束之后是成功还是失败,状态一经改变不可逆转
本人模拟Promise写的MyPromise代码如下:
const PINDING = "pinding"
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(callback) {
try {
//执行回调函数
callback(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
//状态
status = PINDING
//成功返回的值
value = undefined
//失败原因
reason = undefined
//成功回调
ResolveCallback = []
//失败回调
RejectCallback = []
//执行成功结果
resolve = (value) => {
//状态不是等待中则停止执行
if (this.status !== PINDING) return
//修改状态
this.status = FULFILLED
//保存返回值
this.value = value
//如果成功回调存在
while (this.ResolveCallback.length) {
this.ResolveCallback.shift()()
}
}
//执行失败结果
reject = (reason) => {
//状态不是等待中则停止执行
if (this.status !== PINDING) return
//修改状态
this.status = REJECTED
//保存失败原因
this.reason = reason
//如果失败回调存在
while (this.RejectCallback.length) this.RejectCallback.shift()()
}
//链式调用
then(ResolveCallback, RejectCallback) {
ResolveCallback = ResolveCallback ? ResolveCallback : value => value;
RejectCallback = RejectCallback ? RejectCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
//执行成功回调
if (this.status == FULFILLED) {
setTimeout(() => {
try {
let value = ResolveCallback(this.value)
isPromiseFn(value, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
//执行失败回调
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
let reason = RejectCallback(this.reason)
isPromiseFn(reason, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 将成功回调和失败回调存储起来
this.ResolveCallback.push(() => {
setTimeout(() => {
try {
let x = ResolveCallback(this.value);
isPromiseFn(x, resolve, reject, promise)
} catch (e) {
reject(e);
}
}, 0)
});
this.RejectCallback.push(() => {
setTimeout(() => {
try {
let value = RejectCallback(this.reason)
isPromiseFn(value, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise
}
finally(callback) {
return this.then((value) => {
return MyPromise.resolve(callback(value)).then(() => value)
}, (reason) => {
return MyPromise.resolve(callback(reason)).then(() => { throw reason })
})
}
catch(RejectCallback) {
return this.then(undefined, RejectCallback)
}
//静态方法 all
static all(array) {
return new MyPromise((resolve, reject) => {
const result = []
let index = 0
function addData(i, value) {
result[i] = value;
index++;
if (index === array.length) resolve(result)
}
for (let i = 0; i < array.length; i++) {
//判断是否为promise对象
if (array[i] instanceof MyPromise) {
//Promise对象
array[i].then((value) => addData(i, value), reject)
} else {
//普通值
addData(i, array[i])
}
}
})
}
//静态方法 race
static race(array) {
return new MyPromise((resolve, reject) => {
let status = false
function SetStatus(value) {
if (status) return
resolve(value)
}
for (let i = 0; i < array.length; i++) {
//判断是否为promise对象
if (array[i] instanceof MyPromise) {
array[i].then((values) => SetStatus(values))
} else {
SetStatus(array[i])
}
}
})
}
//静态方法 resolve
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
//静态方法 reject
static reject(error) {
if (error instanceof MyPromise) return error
return new MyPromise((resolve, reject) => {
reject(error)
})
}
}
function isPromiseFn(x, resolve, reject, promsie2) {
if (promsie2 === x) {
return reject(new TypeError('then函数不能调用本身的Promise对象'))
}
if (x instanceof MyPromise) {
// promise 对象
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise
2. Promise基本用法
2.1. 实例化Promise
实例化Promise的时候可以传入一个回调函数,这个回调函数会返回2个参数:resolve,reject
2.1.1. resolve
resolve可以用来将Promise对象状态改为fulfilled(成功),并且将值返回到Promise实例上,如以下代码所示:
逻辑示意:
//执行成功结果
resolve = (value) => {
//状态不是等待中则停止执行
if (this.status !== PINDING) return
//修改状态
this.status = FULFILLED
//保存返回值
this.value = value
//如果成功回调存在
while (this.ResolveCallback.length) this.ResolveCallback.shift()()
}
使用示例:
const p1 = new Mypromise((resolve, reject) => {
resolve("p1")
})
p1.then((value) => {
console.log(value)
})
2.1.2. reject
resolve方法用于将Promise对象的状态修改为rejected(失败),并且将失败原因返回到Promise实例上.代码如下所示:
逻辑示意:
//执行失败结果
reject = (reason) => {
//状态不是等待中则停止执行
if (this.status !== PINDING) return
//修改状态
this.status = REJECTED
//保存失败原因
this.reason = reason
//如果失败回调存在
while (this.RejectCallback.length) this.RejectCallback.shift()()
}
使用示例:
const p1 = new Mypromise((resolve, reject) => {
reject("失败")
})
p1.then(() => {
console.log(value)
}, (error) => {
console.log(error)
})
2.2. then链式调用
2.2.1. 基本用法
then方法用来获取Promise实例化以后的Promise对象的返回值或者失败原因,then链式调用主要用于解决普通异步方案中的嵌套回调函数导致的回调地狱问题,使回调函数扁平化,便于阅读和提高代码的可靠性
then方法可以传入2个回调函数,第一个用于处理Promise成功状态的处理,第二个用于Promise失败状态的处理,代码如下图所示:
逻辑代码示例:
//链式调用
then(ResolveCallback, RejectCallback) {
//成功回调不存在则默认返回value
ResolveCallback = ResolveCallback ? ResolveCallback : value => value;
//失败回调不存在则默认返回失败原因
RejectCallback = RejectCallback ? RejectCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
//执行成功回调
if (this.status == FULFILLED) {
setTimeout(() => {
try {
let value = ResolveCallback(this.value)
isPromiseFn(value, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
//执行失败回调
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
let reason = RejectCallback(this.reason)
isPromiseFn(reason, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 将成功回调和失败回调存储起来
this.ResolveCallback.push(() => {
setTimeout(() => {
try {
let x = ResolveCallback(this.value);
isPromiseFn(x, resolve, reject, promise)
} catch (e) {
reject(e);
}
}, 0)
});
this.RejectCallback.push(() => {
setTimeout(() => {
try {
let value = RejectCallback(this.reason)
isPromiseFn(value, resolve, reject, promise)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise
}
使用示例:
const p1 = new Mypromise((resolve, reject) => {
resolve("成功")
})
p1.then((value) => {
console.log(value, "then1")
return 1
}).then((value) => {
console.log(value, "then2")
return 2
}).then((value) => {
console.log(value, "then3")
})
2.2.2. 延时场景
当其中一个then方法延迟执行时,后面的then方法都会等待当前的延时then方法执行完成
const p1 = new Mypromise((resolve, reject) => {
resolve("成功")
})
function other() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
resolve("other")
}, 5000)
})
}
p1.then((value) => {
console.log(value, "then1")
return 1
}).then((value) => {
console.log(value, "then2")
return other()
}).then((value) => {
console.log(value, "then3")
})
执行效果:
2.3. catch方法
catch方法用于使用在then链式调用的末尾,传入一个回调函数,返回参数为失败原因,与reject类似,专门用于处理Promise对象失败逻辑,就减少在每个then方法中使用reject失败回调的频率,如以下代码为例:
逻辑示意:
catch(RejectCallback) {
return this.then(undefined, RejectCallback)
}
使用示例:
const p1 = new Mypromise((resolve, reject) => {
setTimeout(() => {
reject("p1")
}, 1000)
})
p1.then().then().catch((error) => {
console.log(error, "error")
})
执行效果:
2.4. finally方法
finally方法不管是失败状态还是成功状态都会触发,传入一个回调函数,参数为执行成功的返回值或者执行失败的错误原因,常见的使用场景为连接数据库时,Promise对象结束时断开连接,以如下代码为例:
逻辑示意:
finally(callback) {
return this.then((value) => {
return MyPromise.resolve(callback(value)).then(() => value)
}, (reason) => {
return MyPromise.resolve(callback(reason)).then(() => { throw reason })
})
}
使用示例:
const p1 = new Mypromise((resolve, reject) => {
reject("失败 p1")
})
const p2 = new Mypromise((resolve, reject) => {
resolve("成功 p2")
})
p1.finally((error) => {
console.log(error)
})
p2.finally((value) => {
console.log(value)
})
执行效果:
2.5. Promise静态方法
静态方法为在构造函数上使用关键字 static定义的函数,只能被构造函数本身访问,实例对象不可访问
Promise中常用的静态方法为以下几个:
2.5.1. all 方法
all通常用于处理多个异步函数执行时序问题,当所有的异步函数处理完成时将按照传入数组顺序,返回同样的处理顺序结果,注意:当有Promise对象执行失败状态为rejected时,将直接返回失败原因
2.5.1.1. 正常场景
逻辑代码示意:
//静态方法 all
static all(array) {
return new MyPromise((resolve, reject) => {
const result = []
let index = 0
function addData(i, value) {
result[i] = value;
index++;
if (index === array.length) resolve(result)
}
for (let i = 0; i < array.length; i++) {
//判断是否为promise对象
if (array[i] instanceof MyPromise) {
//Promise对象
array[i].then((value) => addData(i, value), reject)
} else {
//普通值
addData(i, array[i])
}
}
})
}
使用示例;
function p1() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
resolve("p1")
}, 4000)
})
}
function p2() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
resolve("🏃🏻")
}, 5000)
})
}
Mypromise.all(["a", "b", p1(), "c", p2()]).then((arr) => {
console.log(arr, "arr")
}, (error) => {
console.log(error, "error")
})
执行效果:
2.5.1.2. 失败场景
当有Promise对象执行失败状态为rejected时,将在失败回调里面直接返回失败的那个Promise对象的原因
示例代码:
function p1() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
reject("p1")
}, 4000)
})
}
function p2() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
resolve("🏃🏻")
}, 5000)
})
}
Mypromise.all(["a", "b", p1(), "c", p2()]).then((arr) => {
console.log(arr, "arr")
}, (error) => {
console.log(error, "error")
})
执行效果:
2.5.2. race方法
race方法通常用于请求超时等需求场景,第一个参数传入需要设置超时的请求方法,第二个参数为设置超时时间设置方法,如以下代码所示: 注意:race方法与all方法一样,当有一个传入的参数执行结果为rejected或者Error时将直接终止运行并返回失败原因
逻辑代码示例:
//静态方法 race
static race(array) {
return new MyPromise((resolve, reject) => {
let status = false
function SetStatus(value) {
if (status) return
resolve(value)
}
for (let i = 0; i < array.length; i++) {
//判断是否为promise对象
if (array[i] instanceof MyPromise) {
array[i].then((values) => SetStatus(values))
} else {
SetStatus(array[i])
}
}
})
}
示例代码:
function apiFetch() {
//此处没有配置接口环境.直接设置10s来模拟大数据请求
return new Mypromise((resolve) => {
setTimeout(() => {
resolve("apiFetch")
}, 10000)
})
}
function p2() {
return new Mypromise((resolve) => {
setTimeout(() => {
resolve("p2")
}, 3000)
})
}
Mypromise.race([apiFetch(), p2()]).then((value) => {
console.log(value, "race")
})
执行效果:
2.5.3. resolve方法
resolve方法用于快速返回一个执行结果为成功状态(fulfilled)的Promise实例,就不需要在去写new Promise一个实例的相关代码了, 与new Promise 中的参数resolve方法作用一致
逻辑代码:
//静态方法 resolve
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
使用示例:
function p1() {
return new Mypromise((resolve) => {
setTimeout(() => {
resolve("p1")
}, 1000)
})
}
Mypromise.resolve(p1()).then((value) => {
console.log(value, "p1")
})
Mypromise.resolve(222).then((value) => {
console.log(value, "基本类型")
})
执行效果:
2.5.4. reject方法
reject方法用于快速返回一个执行结果为成功状态(rejected)的Promise实例,就不需要在去写new Promise一个实例的相关代码了, 与new Promise 中的参数reject方法作用一致
逻辑示意:
//静态方法 reject
static reject(error) {
if (error instanceof MyPromise) return error
return new MyPromise((resolve, reject) => {
reject(error)
})
}
使用示例:
function p1() {
return new Mypromise((resolve, reject) => {
setTimeout(() => {
reject("p1")
}, 1000)
})
}
Mypromise.reject(p1()).then(() => { }, (error) => {
console.log(error, "Promise")
})
Mypromise.reject(222).then(() => { }, (error) => {
console.log(error, "基本类型")
})
执行效果:
3. 配合async await 使用
时下异步编程的最优解决方案为 async await,Promise配合async await使用将如虎添翼,让异步代码以同步的执行方式来编程提高开发效率和代码容错率,还可以解决地狱回调和地狱级链式调用的问题,如下所示:
3.1.1. 没有使用 async await的代码
3.1.2. 使用了 async await的代码
function p1() {
return new Mypromise((resolve) => {
setTimeout(() => {
resolve("p1")
}, 1000)
})
}
function p2() {
return new Mypromise((resolve) => {
setTimeout(() => {
resolve("p2")
}, 5000)
})
}
//核心逻辑
async function Start() {
const A = await p1()
console.log(A)
const B = await p2()
console.log(B)
}
Start()
可以明显看出我们的核心逻辑代码将简洁很多,将多余的业务都抽离了出去,不需要去关心它异步执行的时间,只需要写我们的主逻辑即可
(未完待续)