- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
在ES6之前,对于一些异步任务的处理始终没有很好的方案可以解决,处理异步的方案可谓是十分混乱,在业务需求下异步请求的套用,就形成了回调地狱,严重影响代码的阅读性。而Promise的出现,给我们统一了规范,解决了之前处理异步任务的许多痛点,并且它友好的使用方式,使之成为了JavaScript一大重点,同时也是面试的高频问点,下面就一起来全面认识一下Promise吧。
如果我们想在一个异步请求之后,拿到请求的结果,在ES6之前我们可以怎么做呢?
比如,给定一个请求地址,希望拿到它请求成功或者失败的结果:
function request(url, successCb, failCb) {
setTimeout(function() {
if (url === '/aaa/bbb') { // 请求成功
let res = [1, 2, 3]
successCb(res)
} else { // 请求失败
let err = 'err message'
failCb(err)
}
})
}
// 调用方式,从回调中拿结果
request('/aaa/bbb', function(res) {
console.log(res)
}, function(err) {
console.log(err)
})
将上面的情况使用Promise来实现一下:
function request(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === '/aaa/bbb') {
let res = [1, 2, 3]
resolve(res) // 请求成功调用resolve
} else {
let err = 'err message'
reject(err) // 请求失败调用reject
}
})
})
}
const p = request('/aaa/bbb')
p.then(res => {
console.log(res) // 拿到resolve传递过来的值
}, err => {
console.log(err) // 拿到reject传递过来的值
})
为什么Promise能够将请求的结果准确的传递到then中的回调函数中,因为Promise其核心就用三种状态来进行管控。
通过上面的案例,可以在浏览器中查看Promise分别在执行resolve和reject后的打印结果和Promise当时处于的状态:
注意:在后续的对Promise的讲述过程中,都需要带着Promise的状态去理解。
executor是在创建Promise是需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数,分别就是resolve和reject。
new Promise((resolve, reject) => {
console.log('我是executor中的代码,我会被立即执行~')
})
通常我们会在executor中确定Promise的状态,而且状态一旦被确定下来,Promise的状态就会被锁死,即Promise的状态一旦修改,就不能再次更改了。
上面聊到了resolve需要传入一个普通值,Promise的状态才会被立即锁定为fulfilled,那么如果传递的不是普通值呢?一般resolve传递以下三类值,会有不同的表现效果。
const p = new Promise((resolve, reject) => {
resolve(123)
})
p.then(res => {
console.log(res) // 123
})
传值二:resolve传入一个Promise,那么这个传入的Promise会决定原来Promise的状态;
传入的Promise调用的是resolve;
const newP = new Promise((resolve, reject) => {
resolve(123)
})
const p = new Promise((resolve, reject) => {
resolve(newP)
})
p.then(res => {
console.log(res) // 123
}, err => {
console.log(err)
})
const newP = new Promise((resolve, reject) => {
reject('err message')
})
const p = new Promise((resolve, reject) => {
resolve(newP)
})
p.then(res => {
console.log(res)
}, err => {
console.log(err) // err message
})
传值三:resolve传入一个特殊对象,该对象中实现了then方法,那么Promise的状态就是对象中then方法执行后的结果来决定的;
then中执行了resolve;
const obj = {
then: function(resolve, reject) {
resolve(123)
}
}
const p = new Promise((resolve, reject) => {
resolve(obj)
})
p.then(res => {
console.log(res) // 123
}, err => {
console.log(err)
})
const obj = {
then: function(resolve, reject) {
reject('err message')
}
}
const p = new Promise((resolve, reject) => {
resolve(obj)
})
p.then(res => {
console.log(res)
}, err => {
console.log(err) // err message
})
Promise的实例方法,就是可以通过其实例对象进行调用的方法。
then方法是Promise实例对象上的一个方法:Promise.prototype.then
(1)then方法接收两个参数
promise.then(res => {
console.log('状态变成fulfilled回调')
}, err => {
console.log('状态变成rejected回调')
})
(2)then方法多次调用
const p = new Promise((resolve, reject) => {
resolve('aaa')
})
p.then(res => {
console.log(res) // aaa
})
p.then(res => {
console.log(res) // aaa
})
p.then(res => {
console.log(res) // aaa
})
(3)then方法中的返回值
then调用本身是有返回值的,并且它的返回值是一个Promise,所以then可以进行链式调用,但是then方法调用的返回值的状态是什么呢?主要是由其返回值决定的。
当then方法中的回调在执行时处于pending状态;
当then方法中的回调返回一个结果时处于fulfilled状态,并且会将结果作为resolve的参数;
返回一个普通的值:这个普通的值会被作为一个新Promise的resolve中的值
p.then(res => {
return 123
// 相当于:
/*
return new Promise((resolve, reject) => {
resolve(123)
})
*/
}).then(res => {
console.log(res) // 123
})
p.then(res => {
const obj = {
then: function(resolve, reject) {
resolve('abc')
}
}
return obj
// 相当于:
/*
return new Promise((resolve, reject) => {
resolve(obj.then)
})
*/
}).then(res => {
console.log(res) // abc
})
p.then(res => {
const newP = new Promise((resolve, reject) => {
resolve(123)
})
return newP
// 相当于:
/*
const newP = new Promise((resolve, reject) => {
resolve(123)
})
return new Promise((resolve, reject) => {
resolve(newP)
})
*/
}).then(res => {
console.log(res) // 123
})
const p = new Promise((resolve, reject) => {
throw new Error('err message')
})
p.then(res => {
console.log(res)
}, err => {
console.log(err) // Error: err message
return new Error('then err message')
}).then(res => {
console.log(res)
}, err => {
console.log(err) // Error: then err message
})
catch方法是Promise实例对象上的一个方法:Promise.prototype.catch
(1)catch方法可多次调用
const p = new Promise((resolve, reject) => {
reject('err message')
})
p.catch(err => {
console.log(err) // err message
})
p.catch(err => {
console.log(err) // err message
})
p.catch(err => {
console.log(err) // err message
})
(2)catch方法的返回值
p.catch(err => {
return 'catch return value'
}).then(res => {
console.log(res) // catch return value
})
p.catch(err => {
throw new Error('catch err message')
}).catch(err => {
console.log(err) // Error: catch err message
})
(3)catch的作用
const p = new Promise((resolve, reject) => {
reject('err message')
})
p.then(res => {
console.log(res)
})
p.catch(err => {
console.log(err)
})
// 方法一:
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 方法二:
p.then(res => {
console.log(res)
}, err => {
console.log(err)
})
finally方法是Promise实例对象上的一个方法:Promise.prototype.finally
const p = new Promise((resolve, reject) => {
resolve(123)
})
p.then(res => {
console.log(res) // 123
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('finally code') // finally code
})
Promise的类方法,就是直接通过Promise进行调用。
resolve方法具体有什么用呢?当我们希望将一个值转成Promise来使用,就可以通过直接调用resolve方法来实现,其效果就相当于在new一个Promise时在executor中执行了resolve方法。
resolve传入的参数类型:
const p = Promise.resolve('aaaa')
// 相当于:
/*
const p = new Promise((resolve, reject) => {
resolve('aaaa')
})
*/
console.log(p)
const p = Promise.resolve({
then: function(resolve, reject) {
resolve('aaaa')
}
})
// 相当于:
/*
const p = new Promise((resolve, reject) => {
resolve({
then: function(resolve, reject) {
resolve('aaaa')
}
})
})
*/
console.log(p)
const p = Promise.resolve(new Promise((resolve, reject) => {
resolve('abc')
}))
// 相当于:
/*
const p = new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
resolve('abc')
}))
})
*/
console.log(p)
reject方法和resolve的用法一致,只不过是将可以得到一个状态为rejected的Promise对象,并且reject不过传入的是什么参数,都会原封不动作为rejected状态传递到catch中。
// 1.传入普通值
const p1 = Promise.reject(123)
p1.then(res => {
console.log(res)
}).catch(err => {
console.log('err:', err)
})
// 2.传入实现then方法对象
const p2 = Promise.reject({
then: function(resolve, reject) {
resolve('aaaa')
}
})
p2.then(res => {
console.log(res)
}).catch(err => {
console.log('err:', err)
})
// 3.传入Promise
const p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('aaaa')
})
)
p3.then(res => {
console.log(res)
}).catch(err => {
console.log('err:', err)
})
all方法可以接收由多个Promise对象组成的数组(准确来说是可接收一个可迭代对象),all方法调用返回的Promise状态,由所有Promise对象共同决定。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})
Promise.all([p1, p2, p3]).then(res => {
console.log('res:', res) // res: [ 111, 222, 333 ]
}).catch(err => {
console.log('err:', err)
})
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})
Promise.all([p1, p2, p3]).then(res => {
console.log('res:', res)
}).catch(err => {
console.log('err:', err) // err: err message
})
相比于all方法,allSettled方法不管传入的Promise对象的状态是fulfilled还是rejected,最终都会讲结果返回,并且返回的结果是一个数组,数组中存放着每一个Promise对应的状态status和对应的值value。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})
Promise.allSettled([p1, p2, p3]).then(res => {
console.log('res:', res)
}).catch(err => {
console.log('err:', err)
})
race翻译为竞争,顾名思义哪一个Promise对象最先返回结果,就使用最先返回结果的Promise状态。
一下代码是p1
最先有结果的,p1
中执行的是resolve,所以返回的状态为fulfilled:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})
Promise.race([p1, p2, p3]).then(res => {
console.log('res:', res) // res: 111
}).catch(err => {
console.log('err:', err)
})
any方法是ES12中新增的方法,与race是类似的,any方法会等到有一个fulfilled状态的Promise,才会决定any调用返回新Promise的状态(也就是说any一定会等到有一个Promise状态为fullfilled)。
那么,如果所有的Promise对象的状态都变为了rejected呢?最终就会报一个AggregateError
错误,如果想拿到所有的rejected状态的返回值,可以通过在捕获异常回调参数中的errors
获取:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message1')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message2')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('err message3')
}, 3000)
})
Promise.any([p1, p2, p3]).then(res => {
console.log('res:', res)
}).catch(err => {
console.log(err)
console.log(err.errors)
})
注意:any方法是ES12新增的,node版本过低的话是会报错找不到any方法的,可以在浏览器中测试。
掌握了以上Promise的用法,那么就一步步来实现一下Promise吧。
// 定义Promise的三种状态常量
const PENDING_STATUS = 'pending'
const FULFILLED_STATUS = 'fulfilled'
const REJECTED_STATUS = 'rejected'
class MyPromise {
constructor(executor) {
// 初始化Promise的状态为pending
this.promiseStatus = PENDING_STATUS
// 初始化变量,用于保存resolve和reject传入的参数值
this.value = undefined
this.reason = undefined
// 1.定义executor需要传入的resolve函数
const resolve = (value) => {
// 只有当Promise的状态为pending,才能将状态改变fulfilled
if (this.promiseStatus === PENDING_STATUS) {
this.promiseStatus = FULFILLED_STATUS
this.value = value
console.log('调用了resolve,状态变成fulfilled啦~')
}
}
// 2.定义executor需要传入的reject函数
const reject = (reason) => {
// 只有当Promise的状态为pending,才能将状态改变为rejected
if (this.promiseStatus === PENDING_STATUS) {
this.promiseStatus = REJECTED_STATUS
this.reason = reason
console.log('调用了reject,状态变成rejected啦~')
}
}
// 3.将定义的两个函数传入executor并调用
executor(resolve, reject)
}
}
简单测试一下:
// 先调用resolve
new MyPromise((resolve, reject) => {
resolve()
reject()
})
// 先调用reject
new MyPromise((resolve, reject) => {
reject()
resolve()
})
(1)then基本实现
then方法接收两个参数:
onFulfilled回调:当Promise状态变为fulfilled需要执行的回调;
onRejected回调:当Promise状态变为rejected需要执行的回调
class MyPromise {
constructor(executor) {
// 初始化Promise的状态为pending
this.promiseStatus = PENDING_STATUS
// 初始化变量,用于保存resolve和reject传入的参数值
this.value = undefined
this.reason = undefined
// 1.定义executor需要传入的resolve函数
const resolve = (value) => {
// 只有当Promise的状态为pending,才能将状态改变fulfilled
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = FULFILLED_STATUS
this.value = value
// 状态变成fulfilled就去调用onFulfilled
this.onFulfilled(this.value)
})
}
}
// 2.定义executor需要传入的reject函数
const reject = (reason) => {
// 只有当Promise的状态为pending,才能将状态改变为rejected
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = REJECTED_STATUS
this.reason = reason
// 状态变成rejected就去调用onRejected
this.onRejected(this.reason)
})
}
}
// 3.将定义的两个函数传入executor并调用
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 保存fulfilled和rejected状态的回调
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
注意:这里将onFulfilled和onRejected的调动放在了queueMicrotask
,在JavaScript中可以通过queueMicrotask
使用微任务,而原Promise的then中回调的执行,也是会被放在微任务中的,为什么要放在微任务中呢?
原因:如果不使用微任务,那么在executor中执行resolve或者reject时,then方法还没被调用,onFulfilled和onRejected就都还没被赋值,所以调用时会报错,加入微任务就可以实现将onFulfilled和onRejected的调用推迟到下一次事件循环,也就是等then调用后赋值了才会执行。
简单测试一下:
const p1 = new MyPromise((resolve, reject) => {
resolve('aaaa')
})
const p2 = new MyPromise((resolve, reject) => {
reject('err message')
})
p1.then(res => {
console.log(res) // aaaa
}, err => {
console.log(err)
})
p2.then(res => {
console.log(res)
}, err => {
console.log(err) // err message
})
(2)then优化一
对于以上then的基本实现,还存在一些不足之处,比如:
then方法是可以进行多次调用的,并且每一次调用都是独立调用,互不影响,所以需要收集当Promise状态改变时,对应需要执行哪些回调,需用数组进行收集;
如果then是放到定时器中调用的,那么改then的回调是不会被调用的,因为在前面我们是通过将回调添加到微任务中执行的,而定时器是宏任务,会在微任务执行完成后执行,所以定时器中then的回调就没有被调用;
当then是放到定时器中执行的,那么执行的时候,微任务已经执行完成了,Promise状态肯定也确定下来了,那么只需要直接调用then中的回调即可;
class MyPromise {
constructor(executor) {
// 初始化Promise的状态为pending
this.promiseStatus = PENDING_STATUS
// 初始化变量,用于保存resolve和reject传入的参数值
this.value = undefined
this.reason = undefined
// 初始化两个数组,分别用于保存then中对应需要执行的回调
this.onFulfilledFns = []
this.onRejectedFns = []
// 1.定义executor需要传入的resolve函数
const resolve = (value) => {
// 只有当Promise的状态为pending,才能将状态改变fulfilled
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = FULFILLED_STATUS
this.value = value
// 状态变成fulfilled就去遍历调用onFulfilled
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
})
}
}
// 2.定义executor需要传入的reject函数
const reject = (reason) => {
// 只有当Promise的状态为pending,才能将状态改变为rejected
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = REJECTED_STATUS
this.reason = reason
// 状态变成rejected就去遍历调用onRejected
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
// 3.将定义的两个函数传入executor并调用
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) {
onFulfilled(this.value)
}
if (this.promiseStatus === REJECTED_STATUS && onRejected) {
onRejected(this.reason)
}
// 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
if (this.promiseStatus === PENDING_STATUS) {
// 将then中成功的回调和失败的回调分别存入数组中
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
}
简单测试一下:
const p = new MyPromise((resolve, reject) => {
resolve('aaaa')
reject('err message')
})
p.then(res => {
console.log('res1:', res)
}, err => {
console.log('err1:', err)
})
p.then(res => {
console.log('res2:', res)
}, err => {
console.log('err1:', err)
})
setTimeout(() => {
p.then(res => {
console.log('res3:', res)
}, err => {
console.log('err1:', err)
})
})
(3)then优化二
try catch
判断是调用resolve还是reject;try catch
去执行executor;class MyPromise {
constructor(executor) {
// 初始化Promise的状态为pending
this.promiseStatus = PENDING_STATUS
// 初始化变量,用于保存resolve和reject传入的参数值
this.value = undefined
this.reason = undefined
// 初始化两个数组,分别用于保存then中对应需要执行的回调
this.onFulfilledFns = []
this.onRejectedFns = []
// 1.定义executor需要传入的resolve函数
const resolve = (value) => {
// 只有当Promise的状态为pending,才能将状态改变fulfilled
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = FULFILLED_STATUS
this.value = value
// 状态变成fulfilled就去遍历调用onFulfilled
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
})
}
}
// 2.定义executor需要传入的reject函数
const reject = (reason) => {
// 只有当Promise的状态为pending,才能将状态改变为rejected
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = REJECTED_STATUS
this.reason = reason
// 状态变成rejected就去遍历调用onRejected
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
// 3.将定义的两个函数传入executor并调用
// 如果executor中就抛出了异常,那么直接执行reject即可
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
if (this.promiseStatus === FULFILLED_STATUS && onFulfilled) {
// 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.promiseStatus === REJECTED_STATUS && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
}
// 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
if (this.promiseStatus === PENDING_STATUS) {
// 将then中成功的回调和失败的回调分别存入数组中
// 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch(err) {
reject(err)
}
})
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch(err) {
reject(err)
}
})
}
})
}
}
简单测试一下:
const p = new MyPromise((resolve, reject) => {
resolve('aaaa')
})
p.then(res => {
console.log('res1:', res)
return 'bbbb'
}, err => {
console.log('err1:', err)
}).then(res => {
console.log('res2:', res)
throw new Error('err message')
}, err => {
console.log('err2:', err)
}).then(res => {
console.log('res3:', res)
}, err => {
console.log('err3:', err)
})
// catch方法实现
catch(onRejected) {
return this.then(undefined, onRejected)
}
then方法改进:
简单测试一下:
const p = new MyPromise((resolve, reject) => {
reject('err message')
})
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // err message
})
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
// 也可直接简写成:
// this.then(onFinally, onFinally)
}
then方法改进:
简单测试一下:
const p = new MyPromise((resolve, reject) => {
resolve('aaaa')
})
p.then(res => {
console.log('res:', res) // res: aaaa
}).catch(err => {
console.log('err:', err)
}).finally(() => {
console.log('我是一定会执行的!') // 我是一定会执行的!
})
static
关键字;static resolve(value) {
return new MyPromise((resolve, reject) => resolve(value))
}
static reject(reasion) {
return new MyPromise((resolve, reject) => reject(reasion))
}
简单测试一下:
MyPromise.resolve('aaaa').then(res => {
console.log(res) // aaaa
})
MyPromise.reject('bbbb').then(res => {
console.log(res)
}, err => {
console.log(err) // bbbb
})
static all(promises) {
return new MyPromise((resolve, reject) => {
// 用于存放所有成功的返回值
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push(res)
// 当成功返回值的长度与传入promises的长度相等,就调用resolve
if (results.length === promises.length) {
resolve(results)
}
}, err => {
// 一旦有一个promise变成了rejected状态,就调用reject
reject(err)
})
})
})
}
简单测试一下:
const p1 = new MyPromise(resolve => {
setTimeout(() => {
resolve('aaaa')
}, 1000)
})
const p2 = new MyPromise(resolve => {
setTimeout(() => {
resolve('bbbb')
}, 2000)
})
const p3 = new MyPromise(resolve => {
setTimeout(() => {
resolve('cccc')
}, 3000)
})
MyPromise.all([p1, p2, p3]).then(res => {
console.log(res) // [ 'aaaa', 'bbbb', 'cccc' ]
}).catch(err => {
console.log(err)
})
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
// 用于存放所有promise的状态和返回值
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: FULFILLED_STATUS, value: res })
// 当长度相等,调用resolve
if (results.length === promises.length) {
resolve(results)
}
}, err => {
results.push({ status: REJECTED_STATUS, value: err })
// 当长度相等,调用resolve
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
简单测试一下:
const p1 = new MyPromise(resolve => {
setTimeout(() => {
resolve('aaaa')
}, 1000)
})
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('err message')
}, 2000)
})
const p3 = new MyPromise(resolve => {
setTimeout(() => {
resolve('bbbb')
}, 3000)
})
MyPromise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
// 得到状态最先改变的promise,调用对应的resolve和reject
promise.then(res => {
resolve(resolve)
}, err => {
reject(err)
})
})
})
}
AggregateError
,并且可以在AggregateError
中的errors
属性中获取所有错误信息;static any(promises) {
return new MyPromise((resolve, reject) => {
// 用于记录状态为rejected的值
const reasons = []
promises.forEach(promise => {
promise.then(res => {
// 当有一个promise变成fulfilled状态就调用resolve
resolve(res)
}, err => {
reasons.push(err)
// 当所有promise都是rejected就调用reject,并且传入AggregateError
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
简单测试一下:
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('err message1')
}, 1000)
})
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('err message2')
}, 2000)
})
const p3 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('err message3')
}, 3000)
})
MyPromise.any([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log('err:', err)
console.log(err.errors)
})
上面已经对Promise的各个功能进行了实现,下面就来整理一下最终的完整版,可以将一些重复的逻辑抽取出去,比如try catch。
// 定义Promise的三种状态常量
const PENDING_STATUS = 'pending'
const FULFILLED_STATUS = 'fulfilled'
const REJECTED_STATUS = 'rejected'
// try catch逻辑抽取
function tryCatchFn(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch (err) {
reject(err)
}
}
class MyPromise {
constructor(executor) {
// 初始化Promise的状态为pending
this.promiseStatus = PENDING_STATUS
// 初始化变量,用于保存resolve和reject传入的参数值
this.value = undefined
this.reason = undefined
// 初始化两个数组,分别用于保存then中对应需要执行的回调
this.onFulfilledFns = []
this.onRejectedFns = []
// 1.定义executor需要传入的resolve函数
const resolve = (value) => {
// 只有当Promise的状态为pending,才能将状态改变fulfilled
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = FULFILLED_STATUS
this.value = value
// 状态变成fulfilled就去遍历调用onFulfilled
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
})
}
}
// 2.定义executor需要传入的reject函数
const reject = (reason) => {
// 只有当Promise的状态为pending,才能将状态改变为rejected
if (this.promiseStatus === PENDING_STATUS) {
// 添加微任务
queueMicrotask(() => {
// 如果Promise状态不为pending,后面的代码就不再执行了
if (this.promiseStatus !== PENDING_STATUS) return
this.promiseStatus = REJECTED_STATUS
this.reason = reason
// 状态变成rejected就去遍历调用onRejected
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
// 3.将定义的两个函数传入executor并调用
// 如果executor中就抛出了异常,那么直接执行reject即可
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// 判断onRejected是否有值,没有值的话直接赋值一个抛出异常的方法,用于传递到下一次then中的失败回调,供catch调用
onRejected = onRejected || (err => {throw err})
// 判断onFulfilled是否有值,避免在使用catch时传入的undefined不会执行,出现断层现象
onFulfilled = onFulfilled || (value => value)
return new MyPromise((resolve, reject) => {
// 1.如果在调用then时,Promise的状态已经确定了,就直接执行回调
if (this.promiseStatus === FULFILLED_STATUS) {
// 通过try catch捕获异常,没有捕获到执行resolve,捕获到执行reject
tryCatchFn(onFulfilled, this.value, resolve, reject)
}
if (this.promiseStatus === REJECTED_STATUS) {
tryCatchFn(onRejected, this.reason, resolve, reject)
}
// 2.如果调用then时,Promise的状态还没确定下来,就放入微任务中执行
if (this.promiseStatus === PENDING_STATUS) {
// 将then中成功的回调和失败的回调分别存入数组中
// 将传入的回调外包裹一层函数,目的是为了这里能够拿到then中回调执行的结果
this.onFulfilledFns.push(() => {
tryCatchFn(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
tryCatchFn(onRejected, this.reason, resolve, reject)
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(onFinally, onFinally)
}
static resolve(value) {
return new MyPromise((resolve, reject) => resolve(value))
}
static reject(reasion) {
return new MyPromise((resolve, reject) => reject(reasion))
}
static all(promises) {
return new MyPromise((resolve, reject) => {
// 用于存放所有成功的返回值
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push(res)
// 当成功返回值的长度与传入promises的长度相等,就调用resolve
if (results.length === promises.length) {
resolve(results)
}
}, err => {
// 一旦有一个promise变成了rejected状态,就调用reject
reject(err)
})
})
})
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
// 用于存放所有promise的状态和返回值
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({ status: FULFILLED_STATUS, value: res })
// 当长度相等,调用resolve
if (results.length === promises.length) {
resolve(results)
}
}, err => {
results.push({ status: REJECTED_STATUS, value: err })
// 当长度相等,调用resolve
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
// 得到状态最先改变的promise,调用对应的resolve和reject
promise.then(resolve, reject)
})
})
}
static any(promises) {
return new MyPromise((resolve, reject) => {
// 用于记录状态为rejected的值
const reasons = []
promises.forEach(promise => {
promise.then(res => {
// 当有一个promise变成fulfilled状态就调用resolve
resolve(res)
}, err => {
reasons.push(err)
// 当所有promise都是rejected就调用reject,并且传入AggregateError
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!