# 一、什么是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()
方法接受一个数组作为参数,p1
、p2
、p3
都是 Promise 实例,如果不是,就会先调用Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成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)
})
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]);
面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,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)
})
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
})
})
}
}