Promse基本用法
function testPrm (){
let promise = new Promise((resolve, reject) =>{
let r = parseInt(Math.random()*10)
if(r%2 ==0){
resolve("成功")
}else {
reject('失败')
}
} )
return promise
}
testPrm().then((res)=>{
console.log(res)
},(rej)=>{
console.log(rej)
})
Promse解决的问题
Promise能够有效的解决js异步回调地狱问题
Promse规范
- promise有三种状态:pending,fulfilled,rejected。
- pending代表等待的状态,在此状态下,可能执行resolve()的方法,也可能执行reject()方法
- fulfilld代表成功态,此状态下执行resolve()方法,
- rejected代表失败态,此状态下执行reject()方法,一旦成功了就不能失败,反过来也是一样
- 每个promsie都有一个then方法
- 如果new promise 报错了会走失败态(throw new Error(‘报错’)也会走失败态)
- 能够链式调用
我们先实现promise的三种状态和成功或者失败后的返回值
class myPromise{
constructor(executor) {
this.state ='pending'; //初始化状态
this.returnVal =undefined; //成功返回值
this.retReson = undefined; //失败返回值
// 成功方法
let resolve = (value) => {
//将状态修改为成功,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'fullFilled'
this.value = value
}
}
// 失败方法
let reject = (reason) => {
//将状态修改为失败,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'rejected'
this.reason = reason
}
}
//保证函数执行时不会报错,如果保存直接调用失败方法
try {
// 执行函数,外接传递两个函数供其执行
executor(resolve, reject)
} catch (err) {
// 失败则直接执行reject函数
reject(err)
}
}
/**
* then 传递两个函数
* onFullFilled 成功方法
* onRejected 失败方法
* */
then(onFullFilled, onRejected){
//同步执行 状态为fulfuilled,执行onFullFilled,传入成功的值
if (this.state == 'fullFilled') {
onFullFilled(this.value)
}
//同步执行 状态为rejected,执行onRejected,传入失败的值
if (this.state == 'rejected') {
onRejected(this.reason)
}
}
}
const p = new myPromise((resolve, reject) => {
let r = parseInt(Math.random()*10)
if(r%2 ==0){
resolve('success') // 走了成功就不会走失败了
}else {
throw new Error('失败')
// 失败了就走resolve throw后下面的不会执行
reject('failed') // 走了失败就不会走成功
}
})
p.then((res) => {
console.log(res)
}, (err) => {
console.log("捕获",err)
})
//多次运行可输出成功 或者失败两种不同结果
运行流程
- new myPromise 传递一个函数,在内部执行,并返回两个函数供外部成功或者失败时调用(class中的resolve,reject ),调用resolve保存了成功传递参数值,reject保存了失败/错误时传递的值。当then去调用时,then传递两个函数(成功和失败函数),根据state值调用对应的函数执行。
运行流程总结:
- new myPromise -->内部执行(resolve, reject) => {}函数 -->在执行resolve/reject–>then函数
异步问题
上面的的代码如果时同步执行是没有问题的,但是一旦遇到异步,then就会拿不到值
const p = new myPromise((resolve, reject) => {
setTimeout(()=>{
resolve('success')
})
})
p.then((res) => {
console.log(res)
}, (err) => {
console.log("捕获",err)
})
//执行后没有打印任何数据
是什么问题导致没有打印我们想要的数据?
- 根据上面的运行流程我们可以发现 当new myPromise时 ,内部执行(resolve, reject) => {},而外部是一个异步(需要等到下一个循环才能执行),所以就会先执行then 在执行 resolve/ reject方法,所以就是最后两步顺序颠倒导致的错误,所以我们只需要将步骤调换即可。
如何解决异步问题?
- 我们可以用setTimeout比上一个延时时间长一点就可以拿到(显然是一个不好的方法)
- 发布订阅模式(即then方法时传入的函数不执行,收集到一个数组中,当 resolve/ reject被调用时再去执行then传入的函数)
发布订阅模式处理异步问题
//解决异步
class myPromise{
constructor(executor) {
this.state ='pending'; //初始化状态
this.returnVal =undefined; //成功返回值
this.retReson = undefined; //失败返回值
this.onResolvedCallbacks = [] //成功的回调函数
this.onRejectedCallbacks = [] //失败的回调函数
// 成功方法
let resolve = (value) => {
//将状态修改为成功,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'fullFilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// 失败方法
let reject = (reason) => {
//将状态修改为失败,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
//保证函数执行时不会报错,如果保存直接调用失败方法
try {
// 执行函数,外接传递两个函数供其执行
executor(resolve, reject)
} catch (err) {
// 失败则直接执行reject函数
reject(err)
}
}
/**
* then 传递两个函数
* onFullFilled 成功方法
* onRejected 失败方法
* */
then(onFullFilled, onRejected){
// 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
if (this.state == 'fullFilled') {
onFullFilled(this.value)
}
// 同步状态为rejected,执行onRejected,传入失败的值
if (this.state == 'rejected') {
onRejected(this.reason)
}
//异步时状态为pending
if(this.state == 'pending'){
/**
* 收集要执行的函数,这块必须要推入一个函数,
* 不然就会直接执行后边拿不到数据了
* */
//成功时订阅的函数集合
this.onResolvedCallbacks.push(()=>{
onFullFilled(this.value)
})
//失败时订阅的函数集合
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
}
}
const p = new myPromise((resolve, reject) => {
setTimeout(()=>{
resolve('success')
},1500)
})
p.then((res) => {
console.log(res)
}, (err) => {
console.log("捕获",err)
})
//-->打印了success
至此关于已经解决了异步问题!
关于then的链式调用问题
我们先来看一下官方的用法
const data= new Promise(((resolve, reject) => {
resolve(100)
}))
data.then((res)=>{
console.log("1",res)
return res*500
}).then((res)=>{
console.log("2",res)
return new Promise(((resolve, reject) => {
resolve(res)
}))
}).then((res)=>{
console.log('3',res)
})
最后输出结果为
1 100
2 50000
3 50000
为什么then能过够链式调用和then里面可能出现的情况?
- then方法是由在new promise时才会有的,那么我们在返回一个新的promse不就好了?
- then里面返回的可能是一个值,还有可能时一个新的promse对象
解决then链式调用返回值是一个值得问题
//then的链式调用问题
class myPromise{
constructor(executor) {
this.state ='pending'; //初始化状态
this.returnVal =undefined; //成功返回值
this.retReson = undefined; //失败返回值
this.onResolvedCallbacks = [] //成功的回调函数
this.onRejectedCallbacks = [] //失败的回调函数
// 成功方法
let resolve = (value) => {
//将状态修改为成功,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'fullFilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// 失败方法
let reject = (reason) => {
//将状态修改为失败,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
//保证函数执行时不会报错,如果保存直接调用失败方法
try {
// 执行函数,外接传递两个函数供其执行
executor(resolve, reject)
} catch (err) {
// 失败则直接执行reject函数
reject(err)
}
}
/**
* then 传递两个函数
* onFullFilled 成功方法
* onRejected 失败方法
* */
then(onFullFilled, onRejected){
let promise = new myPromise((reslove,reject)=>{
// 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
if (this.state == 'fullFilled') {
reslove(onFullFilled(this.value))
}
// 同步状态为rejected,执行onRejected,传入失败的值
if (this.state == 'rejected') {
reject(onRejected(this.reason))
}
//异步时状态为pending
if(this.state == 'pending'){
/**
* 收集要执行的函数,这块必须要推入一个函数,
* 不然就会直接执行后边拿不到数据了
* */
//成功时订阅的函数集合
this.onResolvedCallbacks.push(()=>{
reslove( onFullFilled(this.value))
})
//失败时订阅的函数集合
this.onRejectedCallbacks.push(()=>{
reject(onRejected(this.reason))
})
}
})
return promise
}
}
const data= new myPromise(((resolve, reject) => {
resolve(100)
}))
data.then((res)=>{
console.log("1",res)
return res*500
}).then((res)=>{
console.log("2",res)
return new myPromise(((resolve, reject) => {
resolve(res)
}))
}).then((res)=>{
console.log('3',res)
})
输出
1 100
2 50000
3 myPromise {
state: 'fullFilled',
returnVal: undefined,
retReson: undefined,
onResolvedCallbacks: [],
onRejectedCallbacks: [],
value: 50000
}
传递是普通值已经可以正常打印了,但是最后一个返回的是一个promise 对象,所以我们这时候需要处理一下,把promise里面的值拿出来,也就是执行promse即可
解决返回值是一个promse对象问题
//执行promise 把值拿出来
const resolvePromise = (promise2, temp, resolve, reject) => {
// x和promise2不能是同一个人,如果是同一个人就报错
if (promise2 === temp) {
return reject(
new TypeError('Chaining cycle detected for promise #<promise>')
)
}
// 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
if (typeof temp === 'object' && temp != null || typeof temp === 'function') {
try {
let then = temp.then // 取then可以报错,报错就走reject()
if (typeof then === 'function') {
// 用then.call()为了避免在使用一次temp.then报错
then.call(temp, y => {
// console.log('y', y)
resolve(y)// 采用promise的成功结果,并且向下传递
}, r => {
reject(r)// 采用promise的失败结果,并且向下传递
})
} else {
resolve(temp)// temp不是一个函数,是一个对象
}
} catch (err) {
reject(err)
}
} else {
// temp是一个普通值
resolve(temp)
}
}
class myPromise{
constructor(executor) {
this.state ='pending'; //初始化状态
this.returnVal =undefined; //成功返回值
this.retReson = undefined; //失败返回值
this.onResolvedCallbacks = [] //成功的回调函数
this.onRejectedCallbacks = [] //失败的回调函数
// 成功方法
let resolve = (value) => {
//将状态修改为成功,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'fullFilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// 失败方法
let reject = (reason) => {
//将状态修改为失败,并存储当前传递的参数
if (this.state == 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
//保证函数执行时不会报错,如果保存直接调用失败方法
try {
// 执行函数,外接传递两个函数供其执行
executor(resolve, reject)
} catch (err) {
// 失败则直接执行reject函数
reject(err)
}
}
/**
* then 传递两个函数
* onFullFilled 成功方法
* onRejected 失败方法
* */
then(onFullFilled, onRejected){
let promise = new myPromise((reslove,reject)=>{
// 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
let temp ;
if (this.state == 'fullFilled') {
//promise 需要等到下一个循环才能拿到所以用了setTimeout,
//也可以用process.next
setTimeout(()=>{
try {
temp = onFullFilled(this.value)
resolvePromise(promise,temp, reslove, reject)
}
catch (err){
reject(err)
}
},0)
}
// 同步状态为rejected,执行onRejected,传入失败的值
if (this.state == 'rejected') {
setTimeout(()=>{
try {
temp =onRejected(this.reason)
resolvePromise(promise,temp, reslove, reject)
}
catch (err){
reject(err)
}
},0)
}
//异步时状态为pending
if(this.state == 'pending'){
/**
* 收集要执行的函数,这块必须要推入一个函数,
* 不然就会直接执行后边拿不到数据了
* */
//成功时订阅的函数集合
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
try {
temp = onFullFilled(this.value)
resolvePromise(promise,temp, reslove, reject)
}catch (err){
reject(err)
}
},0)
})
//失败时订阅的函数集合
this.onRejectedCallbacks.push(()=>{
setTimeout(()=>{
try {
temp = onRejected(this.reason)
resolvePromise(promise,temp, reslove, reject)
}catch (err){
reject(err)
}
},0)
})
}
})
return promise
}
}
const data= new myPromise(((resolve, reject) => {
resolve(100)
}))
data.then((res)=>{
console.log("1",res)
return res*500
}).then((res)=>{
console.log("2",res)
return new myPromise(((resolve, reject) => {
resolve(res)
}))
},(err)=>{
console.log(err)
}).then((res)=>{
console.log('3',res)
})
输出
1 100
2 50000
3 50000
至此核心功能已经实现了!
细节总结:
then方法中为什么要用setTimeout?因为在当前函数内部是无法拿到外部promise这个变量的,需要等到下一次循环事件之后才可以拿到。
catch用法
new Promise((resolve, reject)=>{
reject('errs')
}).then((res)=>{
},(err)=>{
console.log('捕获err',err)
}).catch((errs)=>{
console.log("catch",errs)
})
如果then中,有第二个错误函数的话,catch不会打印输入,
如果没有的话catch 就会执行
catch核心代码
myPromise.prototype.catch = function(onReJected) {
return this.then(undefined, onReJected)
}
只需要让它调用then方法中的错误时要执行的方法即可
由于then方法需要传递两个函数所以需要改成可选参数
//如果 传递一个参数,调用then就会因为少一个函数而报错,所以需要认为的赋值一个函数供他执行
onRejected = isFunction(onRejected) ? onRejected : err => {
throw err
}
修改过后的全部代码
/**
* 整理过后的完整代码
*/
const isFunction = (value) => typeof value === 'function'
const PENDING = 'pending';
const FULLFILLED ='fullFilled';
const REJECTED ='rejected';
const resolvePromise = (promise2, temp, resolve, reject) => {
// x和promise2不能是同一个人,如果是同一个人就报错
if (promise2 === temp) {
return reject(
new TypeError('Chaining cycle detected for promise #<promise>')
)
}
// 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
if (typeof temp === 'object' && temp != null || typeof temp === 'function') {
try {
let then = temp.then // 取then可以报错,报错就走reject()
if (typeof then === 'function') {
// 用then.call()为了避免在使用一次temp.then报错
then.call(temp, y => {
//返回的时一个promise对象,所以需要再次调用
resolvePromise(promise2, y, resolve, reject)
}, r => {
reject(r)// 采用promise的失败结果,并且向下传递
})
} else {
resolve(temp)// x不是一个函数,是一个对象
}
} catch (err) {
reject(err)
}
} else {
// temp是一个普通值
resolve(temp)
}
}
class myPromise{
constructor(executor) {
this.state =PENDING; //初始化状态
this.returnVal =undefined; //成功返回值
this.retReson = undefined; //失败返回值
this.onResolvedCallbacks = [] //成功的回调函数
this.onRejectedCallbacks = [] //失败的回调函数
// 成功方法
let resolve = (value) => {
//将状态修改为成功,并存储当前传递的参数
if (this.state === PENDING) {
this.state = FULLFILLED
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// 失败方法
let reject = (reason) => {
//将状态修改为失败,并存储当前传递的参数
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
//保证函数执行时不会报错,如果保存直接调用失败方法
try {
// 执行函数,外接传递两个函数供其执行
executor(resolve, reject)
} catch (err) {
// 失败则直接执行reject函数
reject(err)
}
}
/**
* then 传递两个函数
* onFullFilled 成功方法
* onRejected 失败方法
* */
then(onFullFilled, onRejected){
onFullFilled = isFunction(onFullFilled) ? onFullFilled : data => data
onRejected = isFunction(onRejected) ? onRejected : err => {
throw err
}
let promise = new myPromise((reslove,reject)=>{
// 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
let temp ;
if (this.state === FULLFILLED) {
setTimeout(()=>{
try {
temp = onFullFilled(this.value)
resolvePromise(promise,temp, reslove, reject)
}
catch (err){
reject(err)
}
},0)
}
// 同步状态为rejected,执行onRejected,传入失败的值
if (this.state === REJECTED) {
setTimeout(()=>{
try {
temp =onRejected(this.reason)
// console.log('temp',temp)
resolvePromise(promise,temp, reslove, reject)
}
catch (err){
console.log('err',err)
reject(err)
}
},0)
}
//异步时状态为pending
if(this.state === PENDING){
/**
* 收集要执行的函数,这块必须要推入一个函数,
* 不然就会直接执行后边拿不到数据了
* */
//成功时订阅的函数集合
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
try {
temp = onFullFilled(this.value)
resolvePromise(promise,temp, reslove, reject)
}catch (err){
reject(err)
}
},0)
})
//失败时订阅的函数集合
this.onRejectedCallbacks.push(()=>{
setTimeout(()=>{
try {
temp = onRejected(this.reason)
resolvePromise(promise,temp, reslove, reject)
}catch (err){
reject(err)
}
},0)
})
}
})
return promise
}
}
myPromise.prototype.catch = function(onReJected) {
// 返回一个没有第一个参数的then方法
return this.then(undefined, onReJected)
}
new myPromise((resolve, reject)=>{
reject('errs')
}).then(undefined,(erss)=>{
console.log('AAA',erss)
}).catch((errs)=>{
console.log("catch",errs)
})
直接catch捕获即可拿到reject返回得数据
promise.all 方法
官方用例
//同步
const a = new Promise((reslove,reject)=>{
reslove('a')
})
const b = new Promise((reslove,reject)=>{
reject('b')
})
Promise.all([a,b]).then((res)=>{
console.log(res)
}).catch((err)=>{
console.log('err',err)
})
//异步
const a = new Promise((reslove,reject)=>{
setTimeout(()=>{
reslove('a')
},1000)
})
const b = new Promise((reslove,reject)=>{
setTimeout(()=>{
reject('b')
},1500)
})
Promise.all([a,b]).then((res)=>{
console.log(res)
}).catch((err)=>{
console.log('err',err)
})
当a和b同时reslove时,all方法才会成功,否则就会被cathc 捕获,如果传入某一个值不是promise对象那就会这个值不会执行会连同promise得结果返回来,遇到错误,返回错误结束。
//判断是否时promise 对象
const isPromise = (value) => {
//value得值不为null 且 typeof 是个obj, 或者是一个函数,在判断value 是否有then方法
if ((value != null && typeof value === 'object') || typeof value === 'function') {
if (typeof value.then == 'function') {
return true
}else {
return false
}
} else {
return false
}
}
static all =(lists)=>{
return new myPromise((resolve, reject) => {
let resDataArr = [];
let index = 0;
let listlen =lists.length;
function processData(i,data){
resDataArr[i] = data
index++;
//如果index 等于lists长度代表最后一个执行完毕可以抛出正确数据
if (index === listlen){
resolve(resDataArr)
}
}
for (let i = 0; i <listlen ; i++) {
if(isPromise(lists[i])){
lists[i].then((data) => {
//此处不能直接resolve,直接执行只能拿到第一个promise得结果,需要临时存储
processData(i, data)
}, (err) => {
//遇到错误直接停止
reject(err)
return
})
}else {
processData(i, lists[i])
}
}
})
}
all方法,能够处理异步和传入的值不是一个promise得处理。
race 实现
原理:循环执行传入的promise ,不是promise 直接输出,如果是promise的话谁先执行先输出谁即可。
static race =(list)=>{
return new myPromise((reslove,reject)=>{
let len = list.length;
for (let i = 0; i <len ; i++) {
console.log("!!!@@@")
if(isPromise(list[i])) {
list[i].then((data) => {
reslove(data)
return
}, (err) => {
reject(err)
return
})
}else {
//非promise直接执行reslove出去
reslove(list[i])
}
}
})
}
目前这个方法有些缺陷,就是如果第一个参数时promise且是异步,第二个是一个函数/其他值时,会打印出第二个值,因为第一个是异步,所以第二个先执行完,所以会打印第二个值,官方中则不会。有兴趣的效果伙伴可以实现一下或者提供一下解决方案!
resolve方法
判断试下传入的值是否是一个promise对象如果是直接返回,如果不是返回一个新的promise对象
static resolve(val){
if(isPromise(val)){
return val
} else {
return new myPromise((reslove,reject)=>{
reslove(val)
})
}
}
resolve方法
与resolve 方法原理相同
static reject(val){
if(isPromise(val)){
return val
}else {
return new myPromise((reslove,reject)=>{
reject(val)
})
}
}
##finally 方法
在ES6中提到(https://es6.ruanyifeng.com/#docs/promise#Promise-prototype-finally)
finally本质上是then方法的特例
myPromise.prototype.finally =function (callback){
let P = this.constructor;
//无论是成功还是失败都会走传入得callback函数
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
}