# 一、什么是Promise

# 1】ES6中一个非常重要和好用的特性就是Promise。

Promise是异步编程的一种解决方案。(基于回调)

什么时候需要处理异步事件

  • 一种很常见的场景应该就是网络请求了
  • 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能相简单的像3+4=7一样将结果返回
  • 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过入的函数回调出去
  • 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦
  • 但是,当网络请求非常复杂时,就会出现回调地狱。
# 3】Promise的优缺点
# 优:
  • 解决异步嵌套问题
  • 解决多个异步并发问题
# 缺:
  • 基于回调
  • promise无法终止异步
  • 终极使用方案:async+await
# 2】网络请求的回调地狱

我们来考虑下面的场景:

  • 我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2
  • 我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3
  • 我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4
  • 发送网络请求url4,获取最终的数据data4
$.ajax('url1',function(data1){
    $.ajax(data1['url2'],function(data2){
        $.ajax(data2['url3'],function(data3){
            $.ajax(data3['url4'],function(data4){
                console.log(data4);
            })
        })
    })
})

上面的代码,正常情况下,不会有什么问题,可以正常运行并且获取我们想要的结果。但是这样的代码难看而且不容易维护。我们可以使用Promise更加优雅的方式来进行这种异步操作

# 3】promise的基本语法

什么情况下会用到Promise

一般情况下是有异步操作时,使用Promise对这个异步操作进行封装

# ①new -> 构造函数(1.保存了一些状态信息 2.执行传入的函数)
# ②在执行传入的回调函数时,会传入两个参数,resolve,reject,他们本身也是函数,resolve配合then()使用,reject配合err()使用
//Promise基本使用
			new Promise((resolve, reject) => {
				setTimeout(() => {
					//成功的时候调用resolve,传入结果
					//resolve('hello')
					
					//失败的时候调用reject,传入结果
					reject('error message')
				}, 1000)
			}).then((data) => {
				//then里面处理成功,此时打印hello
				console.log(data)
			}).catch((err)=>{
				//catch里面处理失败,此时打印error message
				console.log(err)
			})

案例

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<script type="text/javascript">
			//1.使用setTimeout
			//			setTimeout(()=>{
			//				console.log('helloworld')
			//			},1000)
			//参数 -> 函数(resolve,reject)
			//resolve和reject本身又是函数
			
//			new Promise((resolve, reject) => {
//				setTimeout(() => {
//					console.log('helloworld')
//
//					setTimeout(() => {
//						console.log('hellojs')
//
//						setTimeout(() => {
//							console.log('hellopyton')
//
//						}, 1000)
//					}, 1000)
//				}, 1000)
//			})


			//链式编程
			new Promise((resolve, reject) => {
				//第一次网络请求的代码
				setTimeout(() => {
					resolve()
				}, 1000)
			}).then(() => {
				//第一次处理结果的代码
				console.log('helloworld')

				return new Promise((resolve, reject) => {
					//第二次网络请求的代码
					setTimeout(() => {
						resolve()
					}, 1000)
				})
			}).then(() => {
				//第二次处理结果的代码
				console.log('hellojs')

				return new Promise((resolve, reject) => {
					//第三次网络请求的代码
					setTimeout(() => {
						resolve()
					}, 1000)
				})
			}).then(() => {
				//第三次处理结果的代码
				console.log('hellopyton')
			})
		</script>
	</body>
</html>

例子二:

new Promise((resolve, reject) => {
  $.ajax('url',function(data){
    resolve(data)
  })
}).then((d) => {
  //then里面处理成功,此时打印resolve中传入的data值(也就是请求url的结果)
  console.log(d)
}).catch((err)=>{
  //catch里面处理失败
  console.log(err)
})

基本用法

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise 新建后就会立即执行

下面是一个Promise对象的简单例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}

timeout(100).then((value) => {
  console.log(value);
});

# 二、Promise三种状态

首先,当我们开发中有异步操作时,就可以给异步操作包装一个Promise

  • 异步操作之后会有三种状态

    pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。

    fulfilled:满足状态,当我们主动回调了resolve函数时,就处于该状态,并且会回调**.then()**

    rejected:拒绝状态,当我们主动回调了reject函数时,就处于该状态,并且会回调**.catch()**

# 三、Promise的另外处理形式

//Promise基本使用
			new Promise((resolve, reject) => {
				setTimeout(() => {
					//成功的时候调用resolve
					//resolve('hello')
					
					//失败的时候调用reject
					reject('error message')
				}, 1000)
			}).then((data) => {
				//then里面可以写两个函数分别处理失败和成功
				console.log(data)
			},(err) => {
                console.log(err)
            })

# 四、Promise链式调用简写

.then 中return 一个promise对象来形成链式调用。(每次执行Promise的时候,.then()都会返回一个新的Promise实例

  • 如果.then()中返回Promise对象,将会把Promise对象中的resolve回调中的值或者reject回调中的值,作为下一次.then()的参数传进去;
  • 而如果是一个普通值,将会把这个普通值作为下一次.then()的参数传递进去,然后去获取。(可用于简写)
//then中返回Promise对象
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('aaa')
  }, 1000)
}).then(data=>{
  console.log(data)//aaa
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
    resolve('bbb')
    }, 1000)
  })
}).then(data=>{
  console.log(data)//bbb
})

//then中返回普通值
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('aaa')
  }, 1000)
}).then(data=>{
  console.log(data)//aaa
  return undefined//不写返回值就是undefined
}).then(data=>{
  console.log(data)//undefined
})

//只有两种情况会执行catch失败,①返回一个reject()的Promise对象,②抛出异常错误

终止Promise(返回一个新的处于等待态的Promise)

//我们想要不进入第二个.then:在第一个then中①抛错误等同于reject,②return一个值也会传递给下一个then
//此时只有重新return一个新的Promise实例让它处于等待态,就可以实现不调用第二次then
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('aaa')
  }, 1000)
}).then(data=>{
  console.log(data)//aaa
  return new Promise(()=>{})
}).then(data=>{//终止,暂时不会执行这个then
  console.log(data)
})
# 请求成功resolve

执行then成功的情况,①返回一个resolve()的Promise对象,②返回一个普通值,这个普通值直接传递给下一个then()

# 1】常规写法
			//wrapped into
			//网络请求:aaa ->自己处理(10行代码)
			//结果处理:aaa111 ->自己处理(10行代码)
			//结果处理:aaa111222 ->自己处理
			new Promise((resolve, reject) => {
				//假设setTimeout是网络请求
				setTimeout(() => {
					resolve('aaa')
				}, 1000)
			}).then(res => {
				//1.自己处理十行代码
				console.log(res, "第一层的10行处理代码")

				//2.对结果进行第一次处理
				return new Promise((resolve, reject) => {
					resolve(res + '111')
				})
			}).then(res => {
				console.log(res, '第二层的10行处理代码')
				
				return new Promise((resolve, reject) => {
					resolve(res + '222')
				})
			}).then(res => {
				console.log(res, '第三层的10行处理代码')
			})
# 2】简写一:return Promise.resolve()
				//可以将return new Promise((resolve)=>{resolve(res)})
				//优雅的简写成return Promise.resolve(res)
				new Promise((resolve, reject) => {
				//假设setTimeout是网络请求
				setTimeout(() => {
					resolve('aaa')
				}, 1000)
			}).then(res => {
				//1.自己处理十行代码
				console.log(res, "第一层的10行处理代码")

				//2.对结果进行第一次处理
				return  Promise.resolve(res+'111')
			}).then(res => {
				console.log(res, '第二层的10行处理代码')
				
				return Promise.resolve(res+'222')
			}).then(res => {
				console.log(res, '第三层的10行处理代码')
			})
# 3】简写二:直接return结果
			//省略掉Promise.resolve		
			//直接return结果,连Promise.resolve()不用写
			new Promise((resolve, reject) => {
				//假设setTimeout是网络请求
				setTimeout(() => {
					resolve('aaa')
				}, 1000)
			}).then(res => {
				//1.自己处理十行代码
				console.log(res, "第一层的10行处理代码")

				//2.对结果进行第一次处理
				return res + '111'
			}).then(res => {
				console.log(res, '第二层的10行处理代码')

				return res + '222'
			}).then(res => {
				console.log(res, '第三层的10行处理代码')
			})
# 请求失败reject

只有两种情况会执行catch失败,①返回一个reject()的Promise对象,②抛出异常错误

# 1】常规写法
			new Promise((resolve, reject) => {
				//假设setTimeout是网络请求
				setTimeout(() => {
					resolve('aaa')
				}, 1000)
			}).then(res => {
				//1.自己处理十行代码
				console.log(res, "第一层的10行处理代码")

				//2.对结果进行第一次处理
				return new Promise((resolve, reject) => {
					reject('error message')
				})
			}).then(res => {
				console.log(res, '第二层的10行处理代码')
				
				return new Promise((resolve, reject) => {
					resolve(res + '222')
				})
			}).then(res => {
				console.log(res, '第三层的10行处理代码')
			}).catch(err => {
                console.log(err)
            })
			//遇到reject直接跳过后面的then,此时的输出结果为:aaa 第一层的10行处理代码 error message
# 2】简写一:return Promise.reject()
							new Promise((resolve, reject) => {
							//假设setTimeout是网络请求
							setTimeout(() => {
								resolve('aaa')
							}, 1000)
						}).then(res => {
							//1.自己处理十行代码
							console.log(res, "第一层的10行处理代码")
			
							//2.对结果进行第一次处理
							return  Promise.reject('error message')
						}).then(res => {
							console.log(res, '第二层的10行处理代码')
							
							return Promise.resolve(res+'222')
						}).then(res => {
							console.log(res, '第三层的10行处理代码')
						}).catch(err => {
                console.log(err)
              })
# 3】简写二:throw 异常
							new Promise((resolve, reject) => {
							//假设setTimeout是网络请求
							setTimeout(() => {
								resolve('aaa')
							}, 1000)
						}).then(res => {
							//1.自己处理十行代码
							console.log(res, "第一层的10行处理代码")
			
							//2.对结果进行第一次处理
							throw 'error message'
						}).then(res => {
							console.log(res, '第二层的10行处理代码')
							
							return Promise.resolve(res+'222')
						}).then(res => {
							console.log(res, '第三层的10行处理代码')
						}).catch(err => {
                console.log(err)
              })

# 五、Promise.resolve方法的使用

# 用法一:

传入的参数是一个普通值,就可以把普通对象转换成Promise对象。

Promise.resolve(1).then(data=>{
  console.log(data)//1
})

# 用法二:

传入的参数是一个Promise对象,则会等待这个Promise对象执行完后再继续执行。

Promise.resolve(
  new Promise((resolve,reject)=>{
  //...todo
  resolve('成功')
  })
)
.then(data=>{//等待Promise.resolve()里面包的Promise对象执行完毕,得到结果
  console.log(data)//成功
})

# 六、Promise的all方法使用

const p = Promise.all([p1, p2, p3]).then(data=>console.log(data));
//打印出p1,p2,p3有序的。

上面代码中,Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

并发执行,但是有序地得到结果

# 1】使用场景:

有多个网络请求时,此时多个网络请求都是异步进行,多个网络请求分别结束之后再处理结果,一个请求失败都不能继续,需要判断。

let isResult1=false
let isResult2=false
//请求一:
$ajax({
    url:'',
    success:function(){
        console.log('结果1')
        isResult1=true
        handleResult()
    }
})
//请求二:
$ajax({
    url:'',
    success:function(){
        console.log('结果2')
        isResult2=true
        handleResult()
    }
})

function handleResult(){
    if(isResult1 && isResult2){
        //处理结果
    }
}
# 2】all方法的使用:

直接在all()方法中使用数组包裹两个异步请求,他可以在两个异步请求都完成之后,在**.then()**统一进行回调处理结果,不需要像1】中的代码一样判断。

			Promise.all([
				new Promise((resolve, reject) => {
					setTimeout(() => {
						resolve('result1')
					}, 1000)
				}),
				new Promise((resolve, reject) => {
					setTimeout(() => {
						resolve('result2')
					}, 2000)
				})
			]).then(results => {
				console.log(results)
			})

课程all方法例子

Promise.all方法的实现原理就是下面方式二

let fs = require('fs')//file System
//文件名 编码 callback

//同时写两个异步方法
fs.readFile('./name.txt','utf8',function(err,data){
  console.log(data)
})

fs.readFile('./age.txt','utf8',function(err,data){
  console.log(data)
})

我希望最终拿到两个异步操作整体的结果,比如:{name:'小明',age:10}

方式一:串行(第一个走完,走第二个)

let info={}
fs.readFile('./name.txt','utf8',function(err,data1){
  fs.readFile('./age.txt','utf8',function(err,data2){
    info.name=data1
    info.age=data2
    console.log(info)//{ name: '雷浩', age: '20' }
  })
})
//缺点:花费的时间更多,而且当第一个出错之后第二个也无法执行

方式二:并行(并发执行)通过回调函数来解决 (after函数)

let info ={}

fs.readFile('./name.txt','utf8',function(err,data){
	info.name=data
  out()
})

fs.readFile('./age.txt','utf8',function(err,data){
  info.age=data
  out()
})

function out(){
  if(Object.keys(info).length===2){
    console.log(info)//{ name: '雷浩', age: '20' }
  }
}
//此时我们的代码看起来很臃肿,因为每次都需要要在不同的异步方法中使用info这个业务变量,此时就需要使用AOP编程思想来切面式编程

fs.readFile('./name.txt', 'utf8', function (err, data) {
    out('name', data)
})

fs.readFile('./age.txt', 'utf8', function (err, data) {
    out('age', data)
})

let out = after(2, function (res) {
    console.log(res);
})

function after(times, callback) {
    let info = {}
    return function (key, value) {
        info[key] = value
        if (--times === 0) {
            callback(info)
        }
    }
}
//{ name: '雷浩', age: '20' }

方式三:发布订阅模式(发布触发事件emit和订阅事件on

订阅的事件有个特点,可以同时绑定订阅多个事件,所以我们就需要用到数组来存放事件;

发布触发事件,可以在任务执行的时候,发布(触发)订阅(绑定)的多个事件;

发布和订阅之间没有任何关系,订阅只是往数组中放事件,发布只是从数组中取事件

发布订阅模式区别于观察者模式,有什么区别?

  • 观察者模式(基于订阅发布模式,而且订阅者和发布者有联系,形成观察者)
  • 发布订阅模式(发布和订阅之间没有任何关系,订阅只是往event对象数组中放事件,发布只是从event对象数组中取事件)

# 七、Promise的race方法

const p = Promise.race([p1, p2, p3]);

面代码中,只要p1p2p3之中有一个实例率先改变状态p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数(then or catch

Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用Promise.resolve()`方法,将参数转为 Promise 实例,再进一步处理。

# 例子:(判断请求超时)

下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

# 八、Promise的执行顺序

1】一旦Promise创建成功会立即执行

2】会先按顺序把所有的同步操作执行完毕,然后才会执行异步操作

3】可以对函数前面加上async关键字设置成异步函数,将进行的异步操作前面附上await关键字进行等待,来让此异步操作之后的同步操作,等到异步方法执行完毕之后再执行

async ()=>{
    await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('hello1')
        },3000)
    }).then(res=>{
        console.log(res)
    })
    console.log('hello2')
}
//如果没有设置关键字的话,将会先打印hello2再打印hello1
//加上异步函数和等待关键字之后,先打印hello1再打印hello2

# 九、手写Promise

let promise = new Promise((resolve,reject)=>{
  resolve('success')//一旦遇到成功就直接跳出,去执行.then
  reject('fail')//如果是遇到抛出错误也算失败,跳出去执行.then。
  //throw new Error('失败')
}).then(data=>{//成功
  console.log(data)
},err=>{//失败
  console.log(err)
})

按照上面的代码,我们自己手写一个Promise类(

const PENDING = 'PENDING' //等待态
const RESOLVED = 'RESOLVED' //成功态
const REJECTED = 'REJECTED' //失败态

//因为所有的Promise都遵循这个规范,
const resolvePromise=(promise2,x,resolve,reject)=>{
  //判断x的值 和 promise2 是不是同一个,如果是同一个就不要再等待了,直接出错即可
  if(x===promise2){
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  //判断数据类型 typeof constructor instanceof toString
  if( (typeof x === 'object' && x !== null) || typeof x === 'function'){
    let called;
    try{
      let then = x.then//取then 有可能这个then属性是通过 Object.defineProperty来定义的
      if(typeof then === 'function'){//当前有then方法 我就姑且认为它是一个Promise对象
        then.call(x,res=>{//能保证这个then是刚才取出来的then ,不会报错
          if(called){
            return
          }
          called=true//反正多次调用成功和失败
          resolvePromise(promise2,res,resolve,reject)//采用成功结果,将值向下传递
          //这个res可能也是一个Promise对象
        },err=>{
          if(called){
            return
          }
          called=true//反正多次调用成功和失败
          reject(err)//采用失败结果,将值向下传递
        })
      }else{//x是普通对象
        resolve(x)//说明x是一个普通的对象,直接成功即可
      }
    }catch(e){
      if(called){
        return
      }
      called=true//反正多次调用成功和失败
      reject(e)
    }
  }else{//x是一个普通值
    resolve(x)//直接让promise2成功即可
  }
}

class Promise {
  //1.看这个属性,能否在原型上使用
  //2.看属性是否公用
  constructor(executor) {
    this.status = PENDING //默认是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) {//then目前有两个参数
    //判断onfulfilled和onrejected是可选参数
    onfulfilled=typeof onfulfilled==='function'?onfulfilled:data=>data
    onrejected=typeof onrejected==='function'?onrejected:err=>{throw err}
    
    //使用递归来形成链式调用,(因为then会返回一个新的Promise对象)
    let promise2=new Promise((resolve,reject)=>{
      
      //解决同步情况
      if(this.status===RESOLVED){//状态为成功态就传入刚才保存的成功值
        setTimeout(()=>{//因为promise2是执行完new Promise之后的返回值,所以必须是异步的才能正确传入promise2
          try{//因为加了定时器,此时是宏任务,不能被上面的try catch给捕获到,所以需要重新捕获
            let x= onfulfilled(this.value)//这个x可能是普通值,也可能是Promise对象
            //判断x的值 ==推断=> 新的Promise的状态
            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){//状态为等待态就需要把成功回调和失败回调存起来,在异步执行resolve()或者reject()的时候,再去执行保存的回调函数。(发布和订阅模式)
        //如果executor是一个异步,就先订阅好
        this.onResolvedCallbacks.push(()=>{//重写push方法AOP
          //TODO..
          setTimeout(()=>{
            try{
              let x=onfulfilled(this.value)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        })
        this.onRejectedCallbacks.push(()=>{//重写push方法AOP
          //TODO..
          setTimeout(()=>{
            try{
              let x=onrejected(this.reason)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        })
      }
      return promise2
    })
  }
  
}

发布订阅模式解决上面异步情况的例子:

let promise = new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('成功')
  },1000)
})//1.创建Promise对象之后,立即执行Promise里面的回调函数,
//如果是resolve或者reject在同步函数中执行,就不需要保存.then中的回调,直接同步执行就可以了
//如果resolve或者reject在异步函数中执行,就需要先把所有.then中的回调暂存到到一个数组中(订阅、绑定),然后当定时器结束执行resolve或者reject的时候,遍历数组执行保存的回调(发布事件)

//因为.then是同步的函数,resolve或reject在异步中执行的时候,就需要保存它,让它能实现异步。

promise.then(data=>{//成功
  console.log(data)
},err=>{//失败
  console.log(err)
})

promise.then(data=>{//成功
  console.log(data)
},err=>{//失败
  console.log(err)
})

promise.then(data=>{//成功
  console.log(data)
},err=>{//失败
  console.log(err)
})

# 十、手写Promise.all方法

并发执行,但是得到结果是有序的。是Promise对象上的静态方法。(全部成功就成功,有任何一个失败就失败了)

const isPromise=(value)=>{//判断all中传入的数组里面的值是不是Promise对象
  if((typeof value === 'object' && value !== null) || typeof value === 'function'){
    if(typeof value.then === 'function'){
      return true
    }
  }else{
    return false
  }
}

Promise.all = function (values){//静态方法可以直接使用的,不需要new对象
  return new Promise((resolve,reject)=>{
    let arr = []
    let index = 0 //解决多个异步的并发问题,需要使用计数器
    function processData(key,value){
      //index++;//在if外面就是index++,在if中当条件就是++index
      arr[key] = value;
      if(++index === values.length){
        resolve(arr)
      }
      //if(values.length===arr.length){
      //  resolve(arr)//不能这样判断,因为异步Promise对象,在values数组的中间,异步请求还没有结束,arr长度和values长度已经相等了,但是arr数组中Promise对象对应结果的位置是空的。
      //}
    }
 
    for(let i in values){//循环体里面有异步,注意必须用let,不能用var
      let current = values[i]
      if(isPromise(current)){
        //如果是Promise对象,则then获取值
        current.then((data)=>{
          processData(i,data)
        },reject);
      }else{//如果不是Promise对象,是一个普通值,则直接输出到数组中
        processData(i,current)
      }
    }
  })
}

简单实现:

Promise.all = function (arr) {
   if(!Array.isArray(promises)){
     throw new TypeError('You must pass array')
   }
    let res = []
    return new Promise((resolve, reject) => {
        let index = 0
        for (let i in arr) {
            Promise.resolve(arr[i]).then(data => {
                res[i] = data
                index++
                if (index == arr.length) {//必须在Promise异步中判断
                    resolve(res)
                }
            })
        }
    })
}
Promise.all([1, 2, new Promise((r) => { setTimeout(() => { r(3) }) }), 4]).then(data => { console.log(data) })
//[ 1, 2, 3, 4 ]

# 十一、手写Promise.race方法

const p = Promise.race([p1, p2,..., pn]);
p.then(data=>data)//data为率先改变状态的返回值。
Promise.race = function (arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('You must pass array')
    }
    return new Promise((resolve, reject) => {
        for (let i in arr) {
            Promise.resolve(arr[i])
                .then(data => {
                    resolve(data)
                })
                .catch(err => {
                    reject(err)
                })
        }
    })
}
Promise.race([
    new Promise((r, j) => { setTimeout(() => { r(1) }, 2000) }),
    new Promise((r, j) => { setTimeout(() => { j(2) }, 1000) }),
    new Promise((r) => { setTimeout(() => { r(3) }, 1500) })
]).then(data => console.log(data)).catch(err => console.log(err))


# 十二、手写Promise.prototype.finally方法

# 用法:

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

let promise=new Promise((resolve,reject)=>{
  reject(1000)
})
.then(res => {console.log(res)})
.catch(err => {console.log(err)})
.finally(() => {console.log('最终的')})//返回一个新的Promise实例,后面可以接着链式调用
.catch(err => {console.log(err)})
//上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,
//都会执行finally方法指定的回调函数。

finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

# .finally()返回Promise实例(传递Promise对象的作用,所以finally就是特殊的then方法)

promise实例使用.finally()finally会返回一个新的Promise对象,这个Promise对象和finally上一个Promise对象中的resolve成功值或reject失败原因一致。然后我们就可以再次在.finally()后面链式调用.then(),去接收结果。

(finally的回调是没有参数的,但是会返回一个新的Promise对象用于传递上一个Promise的结果)

//例子一:
let promise = new Promise((resolve, reject) => {
  resolve(1000)
})
.finally(() => {
  console.log('最终的');
})//返回一个新的Promise对象(这个Promise对象和promise实例返回的Promise对象一样)
.then(res => console.log(res), err => console.log(err))//接收resolve(1000)
//结果:
//最终的
//1000

//例子二:
let promise = new Promise((resolve, reject) => {
  resolve(1000)
})
.then(res => {console.log(res)}, err => console.log(err))//接收resolve的1000
.finally(() => {
  console.log('最终的');
})//返回一个新的Promise对象(这个Promise对象和上面.then返回的Promise对象一样)
.then(res => console.log(res), err => console.log(err))//接收undefined
//结果:
//1000
//最终的
//undefined

//例子三:
let promise = new Promise((resolve, reject) => {
  resolve(1000)
})
.then(res => {console.log(res);return Promise.resolve(10)}, err => console.log(err))//接收resolve的1000
.finally(() => {
  console.log('最终的');
})//返回一个新的Promise对象(这个Promise对象和上面.then返回的Promise对象一样)
.then(res => console.log(res), err => console.log(err))//接收上一个Promise对象中的resolve(10)
//结果:
//1000
//最终的
//10

# 手写:

class Promise {
  //...
  //原型上的方法
  then(){
    //..
  }
  
  finally(callback){//finally就是一个特殊的then方法
    return this.then(data=>{
      //使用Promise.resolve来解决传入的回调是异步的
      return Promise.resolve(callback()).then(()=>data)
    },err=>{
      return Promise.resolve(callback()).then(()=>{
        throw err
      })
    })
  }
  
}
Last Updated: 8/23/2020, 12:26:56 PM