Promise学习笔记
- 一.Promise是什么?
- 二.为什么使用Promise?Promise的优点
- 三.Promise的状态改变
- 四.Promise对象的值——实例对象中的另一个属性PromiseResult
- 五.Promise的基本流程
- 六.如何使用Promise
- 七. 自定义(手写)Promise
- 7.1 初始结构搭建
- 7.2 resolve与reject结构搭建
- 7.3 resolve与reject代码实现
- 7.4 throw抛出异常改变状态
- 7.5 Promise对象状态只能修改一次
- 7.6 then方法执行回调
- 7.7 异步任务回调的执行
- 7.8 指定多个回调的实现
- 7.9 同步任务中实现 then()的返回结果
- 7.10 异步任务中实现 then()的返回结果
- 7.11 then()方法完善与优化
- 7.12 catch()方法-异常穿透与值传递
- 7.13 resolve方法封装
- 7.14 reject方法封装
- 7.15 all方法封装
- 7.16 race方法封装
- 7.17 then()方法回调的异步执行
- 7.18 class版本的实现
- 7.19 封装Promise.all
- 7.19 封装Promise.race
- 八.async
- 九.await
- 十.async和await结合实践
- 十一.async与await结合发生ajax请求
- defer和async区别
一.Promise是什么?
1.抽象表达:promise是一门新的技术(ES6规范),是js中进行异步编程的新解决方案(旧方案是单纯使用回调函数)
2.具体表达:从语法上说是一个构造函数,从功能上说promise对象用来封装一个异步操作并可以取得成功/失败的结果值。
3.异步编程包括但不限于:
(1)fs文件操作(这个fs时node下面的一个模块 可以对计算机的磁盘进行读写操作)
require('fs').readFile('./index.html',(err,data)=>{})
(2)数据库操作:mongoDB MySQL数据库操作
(3)AJAX网络请求
$.get('/server',(data)=>{})
(4)定时器
setTimeout(()=>{},2000)
由此可见 在promise await async 生成器之前都是使用单纯的回调函数进行异步操作
二.为什么使用Promise?Promise的优点
1.promise使用回调函数更灵活。
旧的回调函数必须在启动异步任务前指定好。
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至能在异步任务结束后指定多个)
2.支持链式调用,可以解决回调地狱问题。
(1)回调地狱:回调函数嵌套调用 外部回调函数异步执行的结果是嵌套的回调执行的条件
(2)回调地狱的缺点:不便于阅读 不便于异常处理(每一层在进入异步任务时 都要对错误进行处理 可能会写很多重复性代码)
(3)解决方案:promise链式调用
三.Promise的状态改变
1.状态是Promise实例对象中的一个内置属性(PromiseState),所以我们不能直接对该属性进行操作
2.PromiseState的三个取值/Promise的三个状态:
(1)pending 未决定的
(2)fullfilled/resolved 成功
(3)rejected 失败
3.Promise的状态改变只存在两种可能,且一个promise对象只能改变一次(例如:状态从pending变为resolved后就不能再改变了),无论变为成功或失败都会有一个结果数据,成功的结果数据称为 value , 失败的结果数据称为 reason :
(1)pending->resolved
(2)pending->rejected
四.Promise对象的值——实例对象中的另一个属性PromiseResult
1.PromiseResult中存的是对象成功/失败的结果(异步任务成功/失败的结果)
2.resolve()和reject()这两个函数可以修改PromiseResult中的值。一旦通过这两个函数设置完PromiseResult的值后 在后续的then方法回调中就可以把值取出来 然后对这个值进行相关的操作
无论成功还是失败 err data都保存在PromiseResult中
五.Promise的基本流程
首先通过new Promise()创建一个对象 在Promise内部封装异步操作 若异步操作成功 则调用resolve函数 并将Promise对象状态改为fullfilled/resolved 在调用then方法时将调用第一个参数 即第一个回调函数中的代码 返回一个新的Promise对象 若Promise内的异步任务失败了 则调用reject函数 并将Promise对象状态改为rejected 失败后将调用then方法中的第二个回调函数 并返回一个新的Promise对象(then方法的返回是一个新的Promise对象)
六.如何使用Promise
6.1 Promise API
6.1.1 Promise的构造函数:Promise(excutor){}
我们可以通过new Promise来实例化对象 在new时 实例化(Promise)需要接受一个参数excutor excutor被称为执行器函数 excutor是一个函数 可以使用箭头函数/匿名函数等来声明 excutor函数中有两个形参resolve和reject resolve和reject也是两个函数 resolve和reject是内部定义的 异步任务成功调用resolve 失败调用reject
1.excutor函数:执行器(resolve,reject)=>{}
2.resolve函数:内部定义成功时我们调用的函数value=>{}
3.reject函数:内部定义失败时我们调用的函数reason=>{}
说明:executor会在promise内部立即同步调用,异步操作在执行器中执行
当代码执行到new Promise((resolve,reject)=>{resolve('ok');})
时 会立即执行(resolve,reject)=>{resolve('ok');}
即(resolve,reject)=>{resolve('ok');}
中的代码是同步调用的 不会进入队列 会立即执行
<script>
let p=new Promise((resolve,reject)=>{
console.log(111);
})
console.log(222);
</script>
//输出结果为:111 222
6.1.2 Promise.prototype.then方法:(onResolved,onRejected)=>{}
then方法用于指定回调 then中有两个参数onResolved和onRejected onResolved和onRejected都是函数
1.onResolved函数:成功的回调函数(value)=>{}
2.onRejected函数:失败的回调函数(reason)=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调 返回一个新的promise对象。
<script>
let p=new Promise((resolve,reject)=>{
let i=1;
if(i==1){
resolve("heihei");
}
else{
reject("no");
}
});
p.then(value => {
console.log(value)
},reason => {
console.warn(reason);
})
</script>
6.1.3 Promise.prototype.catch方法:(onRejected)=>{}
catch方法用于指定回调 不过他只能指定失败的回调 内部由then方法实现 相当于: then(undefined, onRejected)。
onRejected函数:失败的回调函数(reason)=>{}
<script>
let p=new Promise((resolve,reject)=>{
let i=2;
if(i==1){
resolve("heihei");
}
else{
reject("no");
}
});
//使用catch
p.catch(reason=>{
console.log(reason);//输出no
})
</script>
6.1.4 Promise.resolve方法:(value)=>{}
resolve是promise函数对象的 而非实例对象 resolve接收一个参数value 返回一个Promise 对象 他的作用是为了快速得到一个Promise 对象 可以封装一个值 将这个值转换为Promise 对象
value: 成功的数据或promise对象
说明:如果传入的参数为非Promise类型的对象,则返回的结果为成功的promise对象,如果传入的参数为Promise对象,则参数的结果决定了resolve的结果。
<script>
//Promise.resolve
//如果传入的参数为非promise对象,则返回的结果为成功的promise对象
let p1=Promise.resolve(521);
console.log(p1);//输出一个promise对象,promiseresult为521,promisestate为fullfilled
//如果传入的参数为promise对象,则参数的结果决定了resolve的结果
let p2=Promise.resolve(new Promise((resolve,reject)=>{
reject('error');
}))
console.log(p2); //输出的结果为一个失败的promise对象 promiseresult为error,promisestate为rejected
//因为是一个失败的promise对象,所以使用catch来捕获,否则报错。我们当前有个失败的promise 没有对应的回调来对这个结果做处理 此时浏览器会报错 所以可以用catch来处理 在catch中传入一个失败的回调 让她对失败的结果做一个处理
p2.catch(reason=>{
console.log(reason);
})
let p3=Promise.resolve(new Promise((resolve,reject)=>{
resolve('ok');
}))
console.log(p3);//输出一个promise对象,promiseresult为ok,promisestate为fullfilled
</script>
6.1.5 Promise.reject方法:(reason)=>{}
reject方法属于promise函数对象的 而非实例对象 reject方法的作用是快速返回一个失败的Promise对象 可以将一个值进行快速的转换 转换成一个Promise类型的数据 只不过这个Promise是失败的 reject方法接收一个参数 无论你传入什么样的参数 返回的都是一个失败的Promise对象 你传入什么 失败的结果就是什么
reason:失败的原因
<script>
//和promise.resolve类似,如果其传入的参数是一个非promise对象,则它的promiseresult为该参数,如果是一个promise对象,则promiseresult为一个promise对象
let p1=Promise.reject(521);
console.log(p1);
//调用了该reject方法,即使参数是一个成功的promise对象也依旧返回一个失败的promise对象
let p2=Promise.reject(new Promise((resolve,reject)=>{
resolve('ok');
}))
console.log(p2);
</script>
6.1.6 Promise.all方法;(promises)=>{}
all方法属于promise函数对象的 而非实例对象 all接收一个参数 这个参数一般为Promise组成的数组 也就是说这个参数(数组)中每一个元素都是一个Promise对象
promises: 包含n个promise的数组
说明:返回一个新的promise,只有(参数中)所有的promise都成功,(返回的promise的状态)才成功,只要(参数中的promise)有一个失败了,(返回的promise的状态)就直接失败。成功的结果是每一个promise成功结果组成的数组,失败的结果是在这个数组中失败的那个promise对象失败的结果。
<script>
let p1=new Promise((resolve,reject)=>{
resolve('ok');
})
let p2=Promise.resolve('ok2');
let p3=Promise.resolve('ok3');
const result=Promise.all([p1,p2,p3]);
//下列输出结果是一个成功的promise对象,promiseresult的值为一个数组
console.log(result);
</script>
<script>
let p1=new Promise((resolve,reject)=>{
resolve('ok');
})
let p2=Promise.reject('error');
let p3=Promise.resolve('ok3');
const result=Promise.all([p1,p2,p3]);
//下列输出结果是一个失败的promise对象,其结果是p2的失败值
console.log(result);
</script>
6.1.7 Promise.race方法:(promises)=>{}
race方法属于promise函数对象的 而非实例对象 race接收一个参数 这个参数是一个由promise对象组成的数组 返回的新的promise对象的状态由参数数组中的第一个改变状态的promise状态来决定 第一个改变状态的promise成功则最终返回的promise对象的状态为成功 第一个改变状态的promise失败则最终返回的promise对象的状态为失败
promises:包含n个promise的数组
说明:返回一个新的promise对象,第一个完成的promise的结果状态就是最终状态
<script>
//p1使用了定时器,所以不是p1第一个完成状态改变
let p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok');
},1000)
})
let p2=Promise.resolve('Success');
let p3=Promise.resolve('ok');
const result=Promise.race([p1,p2,p3]);
console.log(result); //输出结果是promise对象,promiseresult为success,因为p2先改变状态
</script>
6.2Promise的几个关键问题
6.2.1 如何改变 promise的状态?
(1) resolve(value):如果当前是pending就会变为resolved。
(2) reject(reason):如果当前是pending就会变为rejected。
(3)抛出异常 throw:如果当前是pending就会变为rejected。
<script>
let p1 = new Promise((resolve,reject)=>{
//resolve('success');
//reject('error');
//throw 'error';
})
console.log(p1);
</script>
6.2.2 一个promise指定多个成功/失败回调函数,都会调用吗?
这个问题相当于:如果我们使用then方法为一个promise方法指定多个回调 这些回调是不是都会执行?
答:当promise对象改变对应状态时 都会调用
<script>
let p = new Promise((resolve,reject)=>{
resolve('success');
})
// 第一次回调
p.then(value=>{
console.log("yes");
})
// 第二次回调
p.then(value=>{
console.log("oh yes");
})
</script>
依次执行了两次成功的回调
6.2.3 改变 promise状态和指定回调函数谁先谁后?
改变promise状态:resolve reject throw
指定回调函数:then catch
这个问题可以理解为:代码在运行时 resolve改变状态先执行还是then方法指定回调函数先执行?
1.都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
当执行器函数中的任务是同步任务 你直接去调resolve的时候 在这种情况下 先改变promise对象的状态 后指定回调
<script>
let p = new Promise((resolve,reject) => {
resolve('ok');
});
p.then(value => {
console.log(value);
},reason => {
console.log('error');
})
</script>
当执行器函数中的任务是异步任务时 也就是说我需要等待一段时间才能改变状态 在这种情况下 先执行then方法指定回调 后改变promise对象的状态 在具体使用promise时 这种情况用的比较多
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok');
},1000)
});
p.then(value => {
console.log(value);
},reason => {
console.log('error');
})
</script>
在上面的代码中 我们延迟1s执行执行器函数中的任务 那么我们可以延迟2s执行then回调函数 此时也是先改变状态再指定回调
2.如何先改状态再指定回调?
(1)在执行器中直接调用resolve()/reject();
(2)延迟更长时间才调用then();
3.什么时候才能得到数据?我的回调函数什么时候才执行?
(1)如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
(2)如果先改变的状态,,那当指定回调时,回调函数就会调用(执行),得到数据
6.2.4 Promise.then()返回的新Promise的结果状态由什么决定?
1.简单表达: then()指定的回调函数执行的结果决定。
2.详细表达:
(1)如果抛出异常,新promise变为rejected,reason为抛出的异常。
(2)如果返回的是非prormise的任意值,新promise变为resolved,value为返回的值。
(3)如果返回的是另一个新promise,此promise的结果就会成为新promise的结果。
<script>
let p = new Promise((resolve,reject) => {
resolve('ok');
});
let result = p.then(value => {
//console.log(value);
//1.抛出错误
//throw '出了问题';//result的状态会变为失败 promiseresult为'出了问题'
//2.如果返回的是非prormise类型的对象
//return 521;//result的状态会变为成功 promiseresult为521
//3.如果返回的是一个promise类型的对象
return new Promise((resolve,reject) => {//这个Promise的状态决定了result的状态 这个Promise的结果就是result的结果
resolve('success');
})
},reason => {
console.warn(reason);
});
console.log(result);
</script>
6.2.5 Promise如何串连多个操作任务?
1.promise的then()返回一个新的promise,所以我们可以在then方法后面接着调用then方法,可以看成then()的链式调用。
2.通过then的链式调用串连多个同步/异步任务。
<script>
let p =new Promise((resolve,reject) => {
resolve("yes");
})
p.then(value => {
return new Promise((resolve,reject)=>{
resolve("oh yes~");
});
}).then(value => {
console.log(value);//输出oh yes~
}).then(value => {
console.log(value);//输出undefined 因为then的返回结果是一个promise 这个promise的状态由她(then)指定的回调函数的返回值来决定 上一个then没有返回值 没有返回值是undefined 所以上一个then返回的是一个成功的promise 且成功的结果就是你这个返回的结果undefined(promise的值时undefined)既然是成功的 那么就会执行这个then 并输出上一个then成功的结果 即undefined
})
</script>
6.2.6 Promise 的异常穿透
1.当使用promise的then链式调用时,可以在最后指定失败的回调。
2.前面任何操作出了异常,都会传到最后失败的回调中处理。
<script>
let p =new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("yes");
},1000);
})
p.then(value => {
throw 'oh No';
}).then(value => {
console.log("123");
}).then(value => {
console.log("456");
}).catch(reason=>{
console.warn(reason);//输出oh No 输出的是第一个promise中失败的结果的值
})
</script>
6.2.7 中断 Promise链
1.当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数。
2.办法:在回调函数中返回一个pendding状态的promise对象。
前面我们介绍了promise链 如下所示 下面的代码会打印输出:789 123 456
<script>
let p =new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("yes");
},1000);
})
p.then(value => {
console.log("789");
}).then(value => {
console.log("123");
}).then(value => {
console.log("456");
}).catch(reason=>{
console.warn(reason);
})
</script>
如果我在打印了789之后就不想打印剩下的了 该怎么办?
<script>
let p =new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("yes");
},1000);
})
p.then(value => {
console.log("789");
return new Promise(()=>{});//返回一个pendding状态的promise对象即可中断Promise链 因为这里返回pendding状态的promise对象时 该then方法返回的结果也是一个pendding状态的promise对象 当该then是个pendding状态的promise对象时 后面的then方法的回调就都不能执行了 因为状态没有改变 后面的这些回调函数都不能执行。若你返回的是其他值 如return false 由于false不是promise对象 所以该then方法会返回一个成功的promise对象 该promise对象的结果(promiseresult)是false 下面的then的回调函数都还是会执行。
}).then(value => {
console.log("123");
}).then(value => {
console.log("456");
}).catch(reason=>{
console.warn(reason);
})
</script>
七. 自定义(手写)Promise
7.1 初始结构搭建
index.html文件
<script>
let p = new Promise((resolve,reject) => {//这个Promise是全局的Promise对象 由她实例化出来的 现在我们要自定义一个Promise 我们就要覆盖全局的Promise 不用内置的了 我们用我们自己封装的 我们先创建个promise.js 再用script标签把她引进来 在promise.js中定义一个Promise方法 这样 我在这里new的就是promise.js中的Promise而不是全局的了
// resolve('ok');
reject("error");
});
p.then(value => {
console.log(value)
},reason => {
console.warn(reason);
})
</script>
在promise.js文件中 自定义Promise函数并添加then方法
function Promise(executor){
}
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
}
以下7.2~7.5省略then方法
7.2 resolve与reject结构搭建
执行器函数在内部是同步调用的 所以在Promise内部要写executor() 在index.html中可以发现构造器函数接收了两个参数resolve reject 这两个参数都是函数 所以Promise内部的构造器函数executor() 也要接收两个参数resolve reject 且这两个参数是函数 那么我们就需要在Promise内部定义resolve reject函数
用户在调用resolve reject函数时传入了参数 所以我们在Promise内部定义resolve reject函数时要给他们加上形参
本节代码如下:
function Promise(executor){//这个Promise就会覆盖掉全局的Promise
//自定义resolve函数,名字不一定用resolve 只要这个名字和下面执行器函数中参数的名字一致即可
function resolve(data){
}
//自定义reject函数
function reject(data){
}
//同步调用【执行器函数】
executor(resolve,reject);
}
7.3 resolve与reject代码实现
resolve函数一执行 Promise的状态(PromiseState)会发生变化 且会设置Promise对象成功的结果(PromiseResult)
PromiseState PromiseResult是实例对象上的一个属性
function Promise(executor){
//用this为实例对象添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve 只要这个名字和下面执行器函数中参数的名字一致即可
function resolve(data){
//this.PromiseState = 'fulfilled'; //这里的this指向window 所以前面要保存实例对象的this
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolved
//改变结果值属性
that.PromiseResult = data;
}
//自定义reject函数
function reject(data){
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult = data;
}
//同步调用【执行器函数】
executor(resolve,reject);
}
7.4 throw抛出异常改变状态
try{ }catch(){ } 才能处理throw抛出的异常 异常肯定是在调用执行器函数的时候抛出 所以将executor(resolve,reject);放在try中 catch中的e就是throw抛出的值 所以我们在设置结果值的时候 直接把e给reject就可以了
function Promise(executor){
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve 只要这个名字和下面执行器函数中参数的名字一致即可
function resolve(data){
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolved
//改变结果值属性
that.PromiseResult =data;
}
//自定义reject函数
function reject(data){
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult =data;
}
try{
//同步调用【执行器函数】
executor(resolve,reject);
}catch(e){
//更改Promise对象为失败
reject(e);
}
}
index.html文件
<script>
let p = new Promise((resolve,reject) => {//这个Promise是全局的Promise对象 由她实例化出来的 现在我们要自定义一个Promise 我们就要覆盖全局的Promise 不用内置的了 我们用我们自己封装的 我们先创建个promise.js 再用script标签把她引进来 在promise.js中定义一个Promise方法 这样 我在这里new的就是promise.js中的Promise而不是全局的了
// resolve('ok');
// reject("error");
throw "Error"//try catch时 这个"Error"会传递给e
});
p.then(value => {
console.log(value)
},reason => {
console.warn(reason);
})
</script>
7.5 Promise对象状态只能修改一次
加个判断即可 判断Promise对象的状态之前是不是已经改过了 没改过才改 改过了不改
function Promise(executor){
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve 只要这个名字和下面执行器函数中参数的名字一致即可
function resolve(data){
//判断状态是否修改过,改过就直接返回
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolve
//改变结果值属性
that.PromiseResult =data;
}
//自定义reject函数
function reject(data){
//判断状态是否修改过,改过就直接返回
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult =data;
}
try{
//同步调用【执行器函数】
executor(resolve,reject);
}catch(e){
//更改Promise对象为失败
reject(e);
}
}
7.6 then方法执行回调
已省略Promise方法的代码
见7.4中的index.html文件中的代码 我们可知 then方法是Promise调用的 所以this指向实例对象p 所以this.PromiseState获取的就是p身上的PromiseState
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
//如果Promise状态为fulfilled回调这个函数
if(this.PromiseState === 'fulfilled'){
//将结果值传入
onResolved(this.PromiseResult);
}
//如果Promise状态为rejected回调这个函数
if(this.PromiseState === 'rejected'){
//将结果值传入
onRejected(this.PromiseResult);
}
}
7.7 异步任务回调的执行
index.html文件代码如下:
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok');
},1000)
});
p.then(value => {
console.log(value);
},reason => {
console.log('error');
})
</script>
异步任务一般先执行回调才改变状态。所以上一步的代码通过判断PromiseState的状态来决定执行哪个回调函数行不通,解决如下:
回调是什么时候执行的?如果是同步任务 立刻改变了对象的状态 那么在then方法调用时执行回调 若是异步任务 那么在改变状态后再执行回调 改变状态是在Promise构造函数中的reject()方法 resolve()方法中改变的 所以应该在reject()方法 resolve()方法中改变结果值属性后调用回调函数 如何在Promise构造函数中的reject()方法 resolve()方法中调用回调函数呢?他们都不在一个作用域 所以我们在then方法中需要保存回调函数 我们先给Promise对象声明一个属性callback用于保存回调函数 可以用一个全局变量来保存 但是这样不方便不灵活 就好比你把私房钱存在了别人那里 所以最好是保存在自己身上 所以我们给Promise对象声明一个属性callback用于保存回调函数 在then方法中保存回调函数 然后在Promise构造函数中的reject()方法 resolve()方法中调用回调函数
// 自定义函数Promise
function Promise(executor){
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 定义callback属性,保存pending状态的回调函数
this.callback = {};
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve
function resolve(data){
//判断状态是否修改过
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolve
//改变结果值属性
that.PromiseResult =data;
//应该在这里调用成功的回调函数
//异步任务成功后执行回调函数
if(that.callback.onResolved){//如果callback中有这个onResolved属性 就执行成功的回调
that.callback.onResolved(data);
}
}
//自定义reject函数
function reject(data){
//判断状态是否修改过,改过就直接返回
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult =data;
//应该在这里调用失败的回调函数
//异步任务失败后执行回调函数
if(that.callback.onRejected){
that.callback.onRejected(data);
}
}
try{
//同步调用【执行器函数】
executor(resolve,reject);
}catch(e){
//更改Promise对象为失败
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
//如果Promise状态为fulfilled回调这个函数
if(this.PromiseState === 'fulfilled'){
//将结果值传入
onResolved(this.PromiseResult);
}
//如果Promise状态为rejected回调这个函数
if(this.PromiseState === 'rejected'){
//将结果值传入
onRejected(this.PromiseResult);
}
//如果Promise状态为pending,保存回调函数
if(this.PromiseState === 'pending'){
this.callback = {
onResolved: onResolved,
onRejected: onRejected
}
}
}
7.8 指定多个回调的实现
在指定多个回调时,如果用7.7写法,最后一个then回调会覆盖掉前面的,我们不能只保存一个回调,如果只保存一个回调的话,最后一个then回调会覆盖掉前面的,所以我们需要将所有的回调都保存下来,解决如下,把保存回调函数的callbacks变为数组,将所有的回调都保存在这个数组中,在调用回调时遍历数组,取出所有的回调执行:
// 自定义函数Promise
function Promise(executor){
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 定义callback属性,保存pending状态的回调函数
this.callbacks = [];
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve
function resolve(data){
//判断状态是否修改过
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolve
//改变结果值属性
that.PromiseResult =data;
//异步任务成功后执行回调函数
that.callbacks.forEach(item=>{
item.onResolved(data);
})
}
//自定义reject函数
function reject(data){
//判断状态是否修改过,改过就直接返回
if(that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult =data;
//异步任务失败后执行回调函数
that.callbacks.forEach(item=>{
item.onRejected(data);
})
}
try{
//同步调用【执行器函数】
executor(resolve,reject);
}catch(e){
//更改Promise对象为失败
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
//如果Promise状态为fulfilled回调这个函数
if(this.PromiseState === 'fulfilled'){
//将结果值传入
onResolved(this.PromiseResult);
}
//如果Promise状态为rejected回调这个函数
if(this.PromiseState === 'rejected'){
//将结果值传入
onRejected(this.PromiseResult);
}
//如果Promise状态为pending,保存回调函数
if(this.PromiseState === 'pending'){
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
}
7.9 同步任务中实现 then()的返回结果
1.同步任务指在执行器函数中 直接通过调resolve(),reject()或者使用throw的方式去改变状态
2.then方法返回结果的规律:then方法的返回结果是由他指定的回调函数的执行结果来决定的 也就是由value => {console.log(value);}
或reason => {console.log('error');}
执行的结果决定 如何实现这个功能?
首先 then方法的返回结果是Promise对象 所以then中所有的代码都应该在return new Promise((resolve, reject) => {}))
中 其次我们要获取回调函数执行的结果 根据结果进行后续操作 若结果不是Promise对象 则结果的对象状态为成功 此时需要把then方法的返回结果对象的状态设为成功 那么直接调resolve(result)方法并将回调函数执行的结果作为参数传递进去 这样才能把结果设为回调函数执行的结果 若结果是Promise对象 既然你是个Promise对象 那你一定可以调用then方法 且你的状态如果是成功你一定走then中的第一个回调函数 你的状态就是then方法的返回结果的状态 那么直接调用resolve(v)并将你的结果作为参数传进去就可以啦 你的状态如果是失败同理。如果是使用throw的方式去改变状态 那么需要try catch来捕获
try catch使用来捕获用户在then函数的第一个回调函数中throw的异常
你第一个回调函数就是onResolved onResolved在try中执行 若onResolved抛出错误 那么try肯定可以捕获到 catch可以拿到你抛出的错误的结果 一旦你抛错 我就要改变返回的Promise对象的状态为失败 所以在catch中直接调用reject(e)并将你抛出的错误作为参数传进去
代码如下 已省略Promise构造函数 省略的函数都是和上一节的一样
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
return new Promise((resolve, reject) => {
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
try {
//获取回调函数执行结果
let result = onResolved(this.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
//将结果值传入
onRejected(this.PromiseResult);
}
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
})
}
注:instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
7.10 异步任务中实现 then()的返回结果
index.html文件代码如下:
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok');
},1000)
});
//在异步任务中 当new Promise实例化对象完成后 在这里p状态一定是pending状态
p.then(value => {
console.log(value);
},reason => {
console.log('error');
})
</script>
在异步任务中 肯定执行if (this.PromiseState === 'pending') {}
中的代码 因为if (this.PromiseState === 'pending') {}
中没有调用reject()或resolve()方法 所以then方法返回结果的状态一定是pending 这样是错的 then方法的返回结果应该由他指定的回调函数的执行结果来决定 回调函数最终是在Promise中执行(下面代码中有标注)。在then方法的if (this.PromiseState === 'pending') {}
中不能单纯的把onResolved onRejected保存起来 比如在上图中 因为是reject 所以肯定走then中的第二个回调函数 reason => {console.log('error');}
会传递给onRejected函数 等状态改变完之后会执行onRejected函数 我们会根据onRejected函数执行结果来改变状态 但我们在onRejected函数中没有调用reject()或resolve()方法 所以此时res的状态一直是pending 做如下修改:
已省略Promise构造函数 省略的函数都是和上一节的一样
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
const that = this;
return new Promise((resolve, reject) => {
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
try {
//将结果值传入
let result = onResolved(this.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
//将结果值传入
onRejected(this.PromiseResult);
}
//在异步任务中 肯定来执行下面的代码
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
try {
//执行成功回调函数
let result = onResolved(that.PromiseResult);
//判断
if (result instanceof Promise) {
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: function () {
try {
//执行成功回调函数
let result = onRejected(that.PromiseResult);
//判断
if (result instanceof Promise) {
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(result);
}
} catch(e){
reject(e);
}
}
})
}
})
}
7.11 then()方法完善与优化
将then方法中的重复代码封装到callback方法中 已省略Promise构造函数 省略的函数都是和上一节的一样
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
const that = this;
return new Promise((resolve, reject) => {
//封装重复的部分
function callback(type){
try {
//将结果值传入
let result = type(that.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
callback(onResolved);
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
callback(onRejected);
}
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}
7.12 catch()方法-异常穿透与值传递
index.html文件代码如下:
<script>
let p = new Promise((resolve,reject) => {
setTimeout(()=>{
reject('ok');
},1000);
})
p.then(value => {
console.log("789");
}).then(value => {
console.log("123");
}).then(value => {
console.log("456");
}).catch(reason=>{
console.warn(reason);
})
</script>
我们前面的代码无法实现异常穿透 会报错:onRejected is not a function 因为异步任务失败后会执行then中第二个回调函数 执行完new Promise后p一定是pending状态 所以在调用then方法时 会保存回调函数 第一个第二个都要保存 第一个成功的回调函数 她有 第二个失败的回调函数 他没传 就是undefined 当他的状态改完之后 他一定会调回调 调回调的时候发现 回调里面只有成功的回调函数 没有失败的回调函数 失败的回调函数是undefined 此时就会报错 一旦报错 第一个then返回的就是失败的Promise 后面的每个then同理 直到最后catch处理了他们失败的结果 所以产生问题的原因就是 她没有传递第二个回调函数 内置的Promise允许用户在使用then方法时不传第二个回调函数 所以我们在封装时也要允许用户可以不传第二个回调函数 所以在then中我们要判断是否传了回调函数:if(typeof onRejected !== 'function'){onRejected = reason =>{throw reason;}}
这行代码相当于加了onRejected = reason =>{throw reason;}
这个回调 即:p.then(value => {console.log("789");},reason =>{throw reason;})
异步回调执行失败后 会返回一个失败的Promise 并抛出失败的异常给下一个then 下一个then中相当于又添加了onRejected = reason =>{throw reason;}
这行代码 又返回一个失败的Promise并抛出失败的异常给下一个then 直到最后catch捕获到异常并解决 这样就实现了异常穿透 内置的还可以实现值传递 即第一个回调函数不传也行 如下:以下代码打印222 333
但我们封装的不能实现这个功能 原因和上面一样 异步回调执行成功了 但是在指定回调这里是undefined 状态一改 一执行onResolved 发现onResolved时undefined 所以会报错:onResolved is not a function 所以在封装then时我们也要为onResolved指定默认函数
已省略Promise构造函数 省略的函数都是和上一节的一样
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
const that = this;
//判断回调参数是否存在
if(typeof onRejected !== 'function'){
onRejected = reason =>{
throw reason;
}
}
if(typeof onResolved !== 'function'){
onResolved = value => value;
}
return new Promise((resolve, reject) => {
//封装重复的部分
function callback(type){
try {
//将结果值传入
let result = type(that.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
callback(onResolved);
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
callback(onRejected);
}
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}
//添加catch方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected);
}
7.13~7.16只展示要在promise.js中添加的函数 已省略Promise构造函数 then函数 和其他添加的函数 省略的函数都是和上一节的一样 代码汇总见7.17
7.13 resolve方法封装
resolve返回一个Promise对象 他的状态由传入的值来决定 如果传入的是非Promise类型的数据 那他的状态就是成功且传入的参数就是她成功的结果值 如果传入的是Promise类型的对象 则她返回的结果由你传入的Promise类型的对象的状态和结果来决定
//添加resolve方法 resolve是属于Promise函数对象的 不是实例对象
Promise.resolve = function(value){
//返回promise对象
return new Promise((resolve,reject) =>{
if(value instanceof Promise){
value.then(v=>{
resolve(v);
},r=>{
reject(r);
})
}else{
resolve(value);
}
})
}
7.14 reject方法封装
无论传入啥只返回一个失败的promise对象。
//添加reject方法
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
});
}
7.15 all方法封装
all()方法返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败。失败了返回那个失败值。
//添加all方法
Promise.all = function(promises){
return new Promise((resolve,reject) => {
//添加变量
let count = 0;
// 存放成功结果数组
let arr =[];
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
count++;
//保存成功结果
arr[i]=v;//这里为什么不用push?因为用push可能导致结果的顺序和传图的数组的顺序不相同 因为他们先后改变状态的时间不一定 可能promise[1]先改变状态 他就最先进到数组中了 那他在arr数组中的下标就不再是2 而是0了
//如果全部成功
if (count === promises.length) {
//状态改为成功
resolve(arr);
}
}, r => {
//能进到证明其为失败
reject(r);
});
}
});
}
7.16 race方法封装
//添加race方法
Promise.race = function(promises){
return new Promise((resolve,reject) => {
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
//状态为成功
resolve(v);
}, r => {
//能进到证明其为失败
reject(r);
})
}
});
}
7.17 then()方法回调的异步执行
正常情况下 上述代码的执行结果是111 333 222 因为then中的指定回调是异步执行的 得等同步代码都执行完毕后才执行 但是在我们封装的Promise中 执行结果为111 222 333 如何解决:封装时把回调函数封装成异步的 用定时器包裹一下就好了 所以在then方法中 当Promise状态为fulfilled rejected时将回调函数设置成异步的 在Promise构造函数中 resolve reject执行回调函数时也需要将回调函数设成异步的
// 自定义函数Promise
function Promise(executor){
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 定义callback属性,保存pending状态的回调函数
this.callbacks = [];
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve
function resolve(data) {
//判断状态是否修改过
if (that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolve
//改变结果值属性
that.PromiseResult = data;
//异步任务成功后执行回调函数
setTimeout(() => {
that.callbacks.forEach(item => {
item.onResolved(data);
})
});
}
//自定义reject函数
function reject(data) {
//判断状态是否修改过,改过就直接返回
if (that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult = data;
//异步任务失败后执行回调函数
setTimeout(() => {
that.callbacks.forEach(item => {
item.onRejected(data);
})
});
}
try{
//同步调用【执行器函数】
executor(resolve,reject);
}catch(e){
//更改Promise对象为失败
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
const that = this;
//判断回调参数是否存在
if(typeof onRejected !== 'function'){
onRejected = reason =>{
throw reason;
}
}
if(typeof onResolved !== 'function'){
onResolved = value => value;
}
return new Promise((resolve, reject) => {
//封装重复的部分
function callback(type){
try {
//将结果值传入
let result = type(that.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
setTimeout(()=>{
callback(onResolved);
});
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
setTimeout(()=>{
callback(onRejected);
});
}
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}
//添加catch 方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected);
}
//添加resolve方法
Promise.resolve = function(value){
//返回promise对象
return new Promise((resolve,reject) =>{
if(value instanceof Promise){
value.then(v=>{
resolve(v);
},r=>{
reject(r);
})
}else{
resolve(value);
}
})
}
//添加reject方法
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
});
}
//添加all方法
Promise.all = function(promises){
return new Promise((resolve,reject) => {
//添加变量
let count = 0;
// 存放成功结果数组
let arr =[];
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
count++;
//保存成功结果
arr[i]=v;
//如果全部成功
if (count === promises.length) {
//状态为成功
resolve(arr);
}
}, r => {
//能进到证明其为失败
reject(r);
});
}
});
}
//添加race方法
Promise.race = function(promises){
return new Promise((resolve,reject) => {
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
//状态为成功
resolve(v);
}, r => {
//能进到证明其为失败
reject(r);
})
}
});
}
7.18 class版本的实现
class Promise {
//构造方法
constructor(executor) {
//添加状态属性与结果值属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 定义callback属性,保存pending状态的回调函数
this.callbacks = [];
//保存实例对象的this值
const that = this;
//自定义resolve函数,名字不一定用resolve
function resolve(data) {
//判断状态是否修改过
if (that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'fulfilled'; // 或者 resolve
//改变结果值属性
that.PromiseResult = data;
//异步任务成功后执行回调函数
setTimeout(() => {
that.callbacks.forEach(item => {
item.onResolved(data);
})
});
}
//自定义reject函数
function reject(data) {
//判断状态是否修改过,改过就直接返回
if (that.PromiseState !== 'pending') return;
//改变状态属性
that.PromiseState = 'rejected';
//改变结果值属性
that.PromiseResult = data;
//异步任务失败后执行回调函数
setTimeout(() => {
that.callbacks.forEach(item => {
item.onRejected(data);
})
});
}
try {
//同步调用【执行器函数】
executor(resolve, reject);
} catch (e) {
//更改Promise对象为失败
reject(e);
}
}
//then方法封装
then(onResolved, onRejected) {
const that = this;
//判断回调参数是否存在
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason;
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
//封装重复的部分
function callback(type) {
try {
//将结果值传入
let result = type(that.PromiseResult);
//判断
if (result instanceof Promise) {
//如果是Promise对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果对象状态为【成功】
resolve(result);
}
} catch (e) {
reject(e);
}
}
//如果Promise状态为fulfilled回调这个函数
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
}
//如果Promise状态为rejected回调这个函数
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
});
}
//如果Promise状态为pending,保存回调函数
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}
//catch 方法
catch(onRejected) {
return this.then(undefined, onRejected);
}
//resolve方法 不属于示例对象 它属于类 也就是我们这个构造函数Promise 所以我们需要用static作为关键字对他进行描述 表明这是个静态成员 它属于类而不属于实例对象
static resolve(value) {
//返回promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(value);
}
})
}
//reject方法
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
//all方法
static all(promises) {
return new Promise((resolve, reject) => {
//添加变量
let count = 0;
// 存放成功结果数组
let arr = [];
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
count++;
//保存成功结果
arr[i] = v;
//如果全部成功
if (count === promises.length) {
//状态为成功
resolve(arr);
}
}, r => {
//能进到证明其为失败
reject(r);
});
}
});
}
//race方法
static race(promises) {
return new Promise((resolve, reject) => {
//遍历全部
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//能进到证明其为成功
//状态为成功
resolve(v);
}, r => {
//能进到证明其为失败
reject(r);
})
}
});
}
}
7.19 封装Promise.all
思路:
- 接收一个 Promise 实例的数组
- 遍历每一个数组元素,如果元素不是 Promise 对象,则使用 Promise.resolve 转成 Promise 对象
- 如果全部成功,状态变为 resolved,返回值将组成一个数组传给回调
- 只要有一个失败,状态就变为 rejected,返回值将直接传递给回调all() 的返回值也是新的 Promise 对象
function promiseAll(promises) {
// promiseAll返回的是一个promise 所以才能在promiseAll后面.then和.catch
// 所以我们要先new一个promise示例
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {// 判断参数是不是数组
return reject(new TypeError('arguments must be an array'));
}
var resolvedCounter = 0;// 计数器 用于统计返回了多少个fulfilled
var promiseNum = promises.length;// 统计传入的数组的长度
var resolvedValues = new Array(promiseNum);// 创建新数组 数组长度是传入的数组的长度
// 这个数组用来存放所有promise的返回值(执行成功的结果)
for (let i = 0; i < promiseNum; i++) {// 遍历传入的数组
// 我怕传入的数组中的元素不是promise
// 所以我用Promise.resolve将他包裹一下使她成为一个promise
// Promise.resolve(promises[i])返回的是fulfilled 则执行then中第一个回调函数
Promise.resolve(promises[i]).then(value => {
resolvedCounter++;// 计数器++
resolvedValues[i] = value;// 将当前返回值存入空数组中
if (resolvedCounter == promiseNum) {
// 传入的promise都是fulfilled 将resolvedValues作为resolved的参数返回
return resolve(resolvedValues)
//我们在外层通过调用promiseAll 在then中就可以拿到resolvedValues
}
}, reason => {
// Promise.resolve(promises[i])返回的是rejected 则执行这个回调函数
// 若第一个promise的返回结果是rejected
// 则直接将第一个promise的执行结果作为reject的参数返回
// 后面的promise都不会执行
return reject(reason)
})
}
})
}
7.19 封装Promise.race
思路:返回一个创建的promise,在该promise的then中遍历每一个数组元素,并且当某个元素有返回,直接执行创建的promise的resolve/reject
function promiseRace(promises) {
if (!Array.isArray(promises)) {//判断传入的参数是不是数组
throw new Error("promises must be an array")
}
// race返回的也是一个promise 所以才能在Promise后面.then和.catch
return new Promise(function (resolve, reject) {
for (let i = 0; i < promiseNum; i++) {
// 遍历传入的数组 数组中不管哪个promise执行成功或执行失败都会影响返回的Promise的状态
// 一旦这个状态拿到了之后就定格了不变了 所以可以实现拿到数组中最先执行的promise的值
// 他就是把每个promise都执行了 执行之后把外层Promise中的resolve和reject
// 在他们分别成功或失败时再去执行 虽然有多个promise 但最终走的我外层的Promise
// 这个外层的Promise决定我当前执行是否结束
Promise.resolve(promises[i]).then(value => {
resolve(value);
}, reason => {
reject(reason);
})
)
})
}
八.async
MDN文档
1.async用来标识函数 形成async函数
2.async函数也是一个函数 只不过他的返回值时一个Promise对象
3.Promise对象的结果由async函数执行的返回值决定。
4.async函数的返回结果跟 then()方法返回结果是一样的
九.await
MDN文档
1.await右侧的表达式一般为promise对象,但也可以是其它的值。
2.如果表达式是promise对象,await返回的是promise成功的值。
3.如果表达式是其它值,直接将此值作为await的返回值。
4.await主要是获取promise对象成功的结果 如果promise对象失败的话就通过try catch 在catch中拿到失败的结果
注意:
1.await 必须写在async函数中,但async 函数中可以没有await 。
2.如果await的promise失败了,就会抛出异常,需要通过try…catch捕获处理。
十.async和await结合实践
需求:将resource文件夹下面的1.html 2.html 3.html文件中的内容获取并拼接 然后打印输出拼接结果
用promise来做 代码如下:
用async与await来做 代码如下:
//util中有一个方法可以将API转换成promise形式的函数
const util = require('util');
const mineReadFile = util.promisefy(fs.readFile);
async function main(){
try{
let data1 = await mineReadFile('./resource/1.html')//读取第一个文件的内容
let data2 = await mineReadFile('./resource/2.html')//读取第二个文件的内容
let data3 = await mineReadFile('./resource/3.html')//读取第三个文件的内容
console.log(data1 + data2 + data3);
}catch(e){
console.log(e);
}
}
main()
十一.async与await结合发生ajax请求
需求:点击按钮获取一句名言。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">获取一句名言</button>
<script>
function sendAJAX(url) {
return new Promise((resolve, reject) => {
//创建对象
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
//初始化
xhr.open('GET', url);
//发送
xhr.send();
//处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//输出响应体
resolve(xhr.response);
} else {
//输出响应状态码
reject(xhr.status);
}
}
}
});
}
var btn = document.querySelector("#btn");
btn.addEventListener('click',async function(){
let word = await sendAJAX("http://poetry.apiopen.top/sentences");
console.log(word);
})
</script>
</body>
</html>
defer和async区别
defer会在文档解析完之后执行 并且多个defer会按照顺序执行 而async则是在js加载好之后就会执行 并且有多个async时 哪个加载好就执行哪个