开发中Promise是及其常用的语法,基本上对于异步的处理大都是通过Promise来进行完成。Promise规范有很多,ES6最终采用的是Promise/A+ 规范,所以以下代码也基本是基于这个规范来进行编写的。
首先我们先列举Promise的所有实例方法跟静态方法
实例方法
then: new Promise((resolve, reject) => {...}).then(() => {console.log('rsolve成功回调')}, () => {console.log('reject失败回调')})
catch: new Promise((resolve, reject) => {...}).catch(() => {console.log('reject失败方法')})
finally: new Promise((resolve, reject) => {...}).finally(() => {console.log('成功失败都进入')})
以上方法调用都将返回新的Promise
静态方法
resolve: Promise.resolve(value)返回Promise实例
reject: Promise.reject(value)返回Promise实例
all: Promise.all(promises): 传入数组格式的Promise并返回新的Promise实例,成功便按照顺序把值返回出来,其中一个失败则直接变成失败
race: Promise.race(promises): 传入数组格式的Promise并返回新的Promise实例,成功与失败取决第一个的完成方式
Promise状态一旦确定变不可再发生变化,有以下三个状态:pending、fulfilled、rejected
Promise在浏览器中的实现是放于微任务队列中的,需要做微任务的处理(JavaScript中的Event Loop(事件循环)机制)
class Promise {
_value
_state = 'pending'
_queue = []
constructor(fn) {
if (typeof fn !== 'function') {
throw 'Promise resolver undefined is not a function'
}
/*
new Promise((resolve, reject) => {
resolve: 成功
reject: 失败
})
*/
fn(this._resolve.bind(this), this._reject.bind(this))
}
// 接收1-2参数,第一个为成功的回调,第二个为失败的回调
then(onFulfilled, onRejected) {
// 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册
if (this._state === 'fulfilled') {
onFulfilled?.(this._value)
return
}
// reject同理
if (this._state === 'rejected') {
onRejected?.(this._value)
return
}
// Promise还没有完成,push到一个队列,到时候完成的时候,执行这个队列里面对应的函数
this._queue.push({
onFulfilled,
onRejected,
})
}
// 接收失败的回调
catch(onRejected) {
// 相当于直接调用then传入失败的回调
this.then(null, onRejected)
}
// 成功与失败都执行的回调
finally(onDone) {
const fn = () => onDone()
this.then(fn, fn)
}
// 成功resolve
_resolve(value) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
this._state = 'fulfilled'
// 把值存起来,当再次调用的时候直接取这个值就行,因为Promise一旦确定就不会发生改变了
this._value = value
// 执行前面.then方法里面push函数形式的参数,这样就执行对应的方法了。
this._queue.forEach((callback) => {
callback.onFulfilled?.(this._value)
})
}
// 失败reject
_reject(error) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
this._state = 'rejected'
this._value = error
this._queue.forEach((callback) => {
callback.onRejected?.(this._value)
})
}
}
调用逻辑:
1.通过then方法传入函数形式的参数,也就是onFulfilled => then((onFulfilled, onRejected) => {...})
2.在then方法中把onFulfilled函数放入_queue这个集合中。 => this._queue.push({ onFulfilled, onRejected })
3.等异步回调完成,执行resolve函数,这个时候就调用_queue收集好的通过then方法注册的函数。统一执行这些函数,这样就达到异步回调完成,执行对应的then方法里面的函数
// 结果打印
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
p.then((res) => {
console.log(res) // => success
})
// reject
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})
p1.catch((res) => {
console.log(res) // => fail
})
// finally
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
p2.finally(() => {
console.log('done') // => done
})
在浏览器中 Promise 完成之后会被推入微任务,所以我们也需要进行这块的处理。浏览器中使用MutationObserver,node可以使用process.nextTick
class Promise {
...
// 推入微任务
_nextTick(fn) {
if (typeof MutationObserver !== 'undefined') { // 浏览器通过MutationObserver实现微任务的效果
// 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
const observer = new MutationObserver(fn)
let count = 1
const textNode = document.createTextNode(String(count))
observer.observe(textNode, {
characterData: true
})
textNode.data = String(++count)
} else if (typeof process.nextTick !== 'undefined') { // node端通过process.nextTick来实现
process.nextTick(fn)
} else {
setTimeout(fn, 0)
}
}
// 成功resolve
_resolve(value) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
// 推入微任务
this._nextTick(() => {
this._state = 'fulfilled'
this._value = value
this._queue.forEach((callback) => {
callback.onFulfilled?.(this._value)
})
})
}
// 失败reject
_reject(error) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
// 推入微任务
this._nextTick(() => {
this._state = 'rejected'
this._value = error
this._queue.forEach((callback) => {
callback.onRejected?.(this._value)
})
})
}
...
}
通常Promise会处理多个异步请求,有时候请求之间是有相互依赖关系的。
例如:
const getUser = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
userId: '123'
})
}, 500)
})
}
const getDataByUser = (userId) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// ....
resolve({a: 1})
}, 500)
})
}
// 使用
getUser().then((user) => {
return getDataByUser(user.userId)
}).then((res) => {
console.log(res)// {a: 1}
})
getDataByUser依赖getUser请求回来的用户信息,这里就需要用到Promise链式的调用,下面我们来改动我们的代码
class Promise {
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this))
}
...
// 1. 这时候then方法需要返回新的Promise了,因为需要进行链式调用,并且下一个then方法接受上一个then方法的值
// 2. 返回的Promise肯定是一个新的Promise,不然就会共用状态跟返回结果了。
// 3. 把上一个then方法中的返回值当做下一个Promise resolve的值
then(onFulfilled, onRejected) {
// 返回新的Promise
return new Promise((resolve, reject) => {
// 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
if (this._state === 'fulfilled' && onFulfilled) {
this._nextTick(onFulfilled.bind(this, this._value))
return
}
if (this._state === 'rejected' && onRejected) {
this._nextTick(onRejected.bind(this, this._value))
return
}
/*
把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联。
这样就能把上一个Promise中的onFulfilled与新的Promise中的resolve两个关联到一块,然后便可以做赋值之类的操作了。reject同理
*/
this._queue.push({
onFulfilled,
onRejected,
resolve,
reject
})
})
}
// reject同理
_resolve(value) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
// 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
// 就是resolve()的值如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
if (typeof value === 'object' && typeof value.then === 'function') {
// 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
// 这样就能取到Promise中的值
// this._resove => obj.onFulfilled?.(this._value)
// this._reject => obj.onRejected?.(this._value)
value.then(this._resolve.bind(this), this._reject.bind(this))
return
}
// 推入微任务
this._nextTick(() => {
this._state = 'fulfilled'
this._value = value
this._queue.forEach((obj) => {
// 接受onFulfilled返回值
const val = obj.onFulfilled?.(this._value)
// reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
// obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
obj.resolve(val)
})
})
}
...
}
调用逻辑:
1.微任务采用MutationObserver跟process.nextTick来进行实现
2.Promise链式调用,这里通过把then方法中的(onFulfilled, onRejected)参数与新返回的Promise中的(resolve, reject)关联到一起。
3.一旦上一个Promise成功,调用onFulfilled函数,就可以把onFulfilled中返回的值,放到新的Promise的resolve中。
4.如果遇到resolve的值是Promise对象,递归进行解析,然后再把值返回出去
完整代码:
class Promise {
_value
_state = 'pending'
_queue = []
constructor(fn) {
if (typeof fn !== 'function') {
throw new Error('Promise resolver undefined is not a function')
}
/*
new Promise((resolve, reject) => {
resolve: 成功
reject: 失败
})
*/
fn(this._resolve.bind(this), this._reject.bind(this))
}
// 接收1-2参数,第一个为成功的回调,第二个为失败的回调
then(onFulfilled, onRejected) {
// 返回新的Promise
return new Promise((resolve, reject) => {
// 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
if (this._state === 'fulfilled' && onFulfilled) {
this._nextTick(onFulfilled.bind(this, this._value))
return
}
if (this._state === 'rejected' && onRejected) {
this._nextTick(onRejected.bind(this, this._value))
return
}
// 把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联
this._queue.push({
onFulfilled,
onRejected,
resolve,
reject
})
})
}
// 接收失败的回调
catch(onRejected) {
return this.then(null, onRejected)
}
// 成功与失败都执行的回调
finally(onDone) {
return this.then((value) => {
onDone()
return value
}, (value) => {
// console.log(value)
onDone()
throw value
})
}
// 推入微任务
_nextTick(fn) {
if (typeof MutationObserver !== 'undefined') { // 浏览器
// 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
const observer = new MutationObserver(fn)
let count = 1
const textNode = document.createTextNode(String(count))
observer.observe(textNode, {
characterData: true
})
textNode.data = String(++count)
} else if (typeof process.nextTick !== 'undefined') { // node
process.nextTick(fn)
} else {
setTimeout(fn, 0)
}
}
// 成功resolve
_resolve(value) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
// 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
// 就是如果resolve()的如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
if (typeof value === 'object' && typeof value.then === 'function') {
// 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
// 这样就能取到Promise中的值
// this._resove => obj.onFulfilled?.(this._value)
// this._reject => obj.onRejected?.(this._value)
value.then(this._resolve.bind(this), this._reject.bind(this))
return
}
// 推入微任务
this._nextTick(() => {
this._state = 'fulfilled'
this._value = value
this._queue.forEach((obj) => {
// 使用try catch 来捕获onFulfilled存在函数内部错误的情况
try {
// 接受onFulfilled返回值,如果不存在,把this._value往下传递
const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value
// reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
// obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
obj.resolve(val)
} catch (e) {
obj.reject(e)
}
})
})
}
// 失败reject
_reject(error) {
if (this._state !== 'pending') return
this._nextTick(() => {
this._state = 'rejected'
this._value = error
this._queue.forEach((obj) => {
try {
const val = obj.onRejected ? obj.onRejected(this._value) : this._value
// 当前 reject执行完毕之后,会返回新的Promise,应该是能正常resolve的,所以这里要用 resolve, 不应该继续使用reject来让下个Promise执行失败流程
obj.resolve(val)
} catch (e) {
obj.reject(e)
}
})
})
}
}
总共有4个静态方法: Promise.resolve、Promise.reject、Promise.all、Promise.race,统一返回的都是新的Promise。
class Promise {
...
/**
* 直接resolve
*/
static resolve(value) {
// 是Promise直接返回
if (value instanceof Promise) {
return value
} else if (typeof value === 'object' && typeof value.then === 'function') {
// 传入的对象含有then方法
const then = value.then
return new Promise((resolve) => {
then.call(value, resolve)
})
} else {
// 正常返回值,直接返回新的Promise在resolve这个值
return new Promise((resolve) => resolve(value))
}
}
/**
* 直接reject, 测试下Promise.reject并没做特殊处理,所以直接返回即可。
*/
static reject(value) {
return new Promise((resolve, reject) => reject(value))
}
/**
* 传入数组格式的`Promise`并返回新的`Promise`实例,成功便按照顺序把值返回出来,其中一个失败则直接变成失败
*/
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0
let arr = []
// 按照对应的下标push到数组里面
promises.forEach((promise, index) => {
// 转换成Promise对象
Promise.resolve(promise).then((res) => {
count++
arr[index] = res
if (count === promises.length) {
resolve(arr)
}
}, err => reject(err))
})
})
}
/**
* 传入数组格式的`Promise`并返回新的`Promise`实例,成功与失败取决第一个的完成方式
*/
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
// 转换成Promise对象
Promise.resolve(promise).then((res) => {
// 谁先执行直接resolve, 或reject
resolve(res)
}, err => reject(err))
})
})
}
...
}
class Promise {
_value
_state = 'pending'
_queue = []
constructor(fn) {
if (typeof fn !== 'function') {
throw new Error('Promise resolver undefined is not a function')
}
/*
new Promise((resolve, reject) => {
resolve: 成功
reject: 失败
})
*/
fn(this._resolve.bind(this), this._reject.bind(this))
}
/**
* 接收1-2参数,第一个为成功的回调,第二个为失败的回调
*
* @param {*} onFulfilled
* @param {*} onRejected
* @return {*}
* @memberof Promise
*/
then(onFulfilled, onRejected) {
// 返回新的Promise
return new Promise((resolve, reject) => {
// 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
if (this._state === 'fulfilled' && onFulfilled) {
this._nextTick(onFulfilled.bind(this, this._value))
return
}
if (this._state === 'rejected' && onRejected) {
this._nextTick(onRejected.bind(this, this._value))
return
}
// 把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联
this._queue.push({
onFulfilled,
onRejected,
resolve,
reject
})
})
}
/**
* 接收失败的回调
*
* @param {*} onRejected
* @return {*}
* @memberof Promise
*/
catch(onRejected) {
return this.then(null, onRejected)
}
/**
* 成功与失败都执行的回调
*
* @param {*} onDone
* @return {*}
* @memberof Promise
*/
finally(onDone) {
return this.then((value) => {
onDone()
return value
}, (value) => {
onDone()
// 直接报错,可以在try catch中捕获错误
throw value
})
}
/**
* 直接resolve
*
* @static
* @param {*} value
* @return {*}
* @memberof Promise
*/
static resolve(value) {
if (value instanceof Promise) {
return value
} else if (typeof value === 'object' && typeof value.then === 'function') {
// 传入的对象含有then方法
const then = value.then
return new Promise((resolve) => {
then.call(value, resolve)
})
} else {
return new Promise((resolve) => resolve(value))
}
}
/**
* 直接reject, 测试下reject在Promise.reject中没做特殊处理
*
* @static
* @param {*} value
* @return {*}
* @memberof Promise
*/
static reject(value) {
return new Promise((resolve, reject) => reject(value))
}
/**
* 传入数组格式的`Promise`并返回新的`Promise`实例,成功便按照顺序把值返回出来,其中一个失败则直接变成失败
*
* @static
* @param {*} promises
* @memberof Promise
*/
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0
let arr = []
if (Array.isArray(promises)) {
if (promises.length === 0) {
return resolve(promises)
}
promises.forEach((promise, index) => {
// 转换成Promise对象
Promise.resolve(promise).then((res) => {
count++
arr[index] = res
if (count === promises.length) {
resolve(arr)
}
}, err => reject(err))
})
return
} else {
reject(`${promises} is not Array`)
}
})
}
/**
* 传入数组格式的`Promise`并返回新的`Promise`实例,成功与失败取决第一个的完成方式
*
* @static
* @param {*} promises
* @return {*}
* @memberof Promise
*/
static race(promises) {
return new Promise((resolve, reject) => {
if (Array.isArray(promises)) {
promises.forEach((promise, index) => {
// 转换成Promise对象
Promise.resolve(promise).then((res) => {
resolve(res)
}, err => reject(err))
})
} else {
reject(`${promises} is not Array`)
}
})
}
// 推入微任务
_nextTick(fn) {
if (typeof MutationObserver !== 'undefined') { // 浏览器
// 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
const observer = new MutationObserver(fn)
let count = 1
const textNode = document.createTextNode(String(count))
observer.observe(textNode, {
characterData: true
})
textNode.data = String(++count)
} else if (typeof process.nextTick !== 'undefined') { // node
process.nextTick(fn)
} else {
setTimeout(fn, 0)
}
}
// 成功resolve
_resolve(value) {
// 状态确定了,就不再发生变化了
if (this._state !== 'pending') return
// 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
// 就是如果resolve()的如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
if (typeof value === 'object' && typeof value.then === 'function') {
// 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
// 这样就能取到Promise中的值
// this._resove => obj.onFulfilled?.(this._value)
// this._reject => obj.onRejected?.(this._value)
value.then(this._resolve.bind(this), this._reject.bind(this))
return
}
// 通过打印测试,如果直接在线程里进行resolve, 状态跟值好像是直接就改变了,并没有执行完主流程,在执行微任务的时候进行修改的。
// 所以把状态改变和值的修改移出了微任务,只有在走回调的时候才通过微任务进行处理
this._state = 'fulfilled'
this._value = value
// 推入微任务
this._nextTick(() => {
this._queue.forEach((obj) => {
// 使用try catch 来捕获onFulfilled存在函数内部错误的情况
try {
// 接受onFulfilled返回值,如果不存在,把this._value往下传递
const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value
// reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
// obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
obj.resolve(val)
} catch (e) {
obj.reject(e)
}
})
})
}
// 失败reject
_reject(error) {
if (this._state !== 'pending') return
this._state = 'rejected'
this._value = error
this._nextTick(() => {
this._queue.forEach((obj) => {
try {
// 用户传入的函数内部错误捕获
if (obj.onRejected) {
const val = obj.onRejected(this._value)
// 当前 reject执行完毕之后,会返回新的Promise,应该是能正常resolve的,所以这里要用 resolve, 不应该继续使用reject来让下个Promise执行失败流程
obj.resolve(val)
} else {
// 递归传递reject错误
obj.reject(this._value)
}
} catch (e) {
obj.reject(e)
}
})
})
}
}
完整演示效果
博客原文地址
本项目完整代码:GitHub
以上就是Promise的实现方案,当然这个跟完整的Promises/A+规范是有区别的。这里只是用做于学习之用。
Promise 想必大家十分熟悉,想想就那么几个 api,可是你真的了解 Promise 吗?本文根据 Promise 的一些知识点总结了十道题,看看你能做对几道。
本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,Promise标准中仅指定了Promise对象的then方法的行为,其它一切我们常见的方法/函数都并没有指定.
Async/Await替代Promise的6个理由:Async/Await是近年来JavaScript添加的最革命性的的特性之一。它会让你发现Promise的语法有多糟糕,而且提供了一个直观的替代方法。
Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一,Promise 是一个构造函数, new Promise 返回一个 promise对象 接收一个excutor执行函数作为参数
这篇文章是考虑如何自己实现一个简单 Promise,用以理解 Promise。和原生 Promise的调用方法一样,支持链式调用,本文实现的方法只能用于参考Promise的原理,还有很多特性没有实现,比如 race,all 方法的实现。
在对数组进行一些遍历操作时,发现有些遍历方法对Promise的反馈并不是我们想要的结果。async/await为Promise的语法糖,文中会直接使用async/await替换Promise;map可以说是对Promise最友好的一个函数了,
最近在使用axios库时遇到了个问题,后端接口报了500错误,但前端并未捕获到。在axios整体配置的代码中,过滤http code时,调用了filter401()、filter500(),但是这里注意并未将两个filter函数的结果返回,也就是并未返回promise,这就是导致问题出现的原因
想必接触过Node的人都知道,Node是以异步(Async)回调著称的,其异步性提高了程序的执行效率,但同时也减少了程序的可读性。如果我们有几个异步操作,并且后一个操作需要前一个操作返回的数据才能执行
你可以在 .then 里面 return 一个 Promise,每次执行 .then 的时候都会自动创建一个新的 Promise,对调用者来说,Promise 的 resolved/rejected 状态是唯一的,Promise 构造函数不是解决方案,使用 Promise.resolve
Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难.本篇大概分为以下步骤:实现简单的同步Promise、增加异步功能、增加链式调用then、增加catch finally方法、增加all race 等方法、实现一个promise的延迟对象defer、最终测试
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!