promise
是什么?
是ES6引入的进行异步编程的新的解决方案,从语法上来说是一个构造函数,可以封装异步的任务且可以对结果进行处理。
旧方案:单纯使用回调函数
异步编程有哪些:
- fs 文件操作
require('fs').readFile('./index.html',(err,data)=>{})
- 数据库操作
- AJAX 网络请求
$.get('/server',(data)=>{})
- 定时器 setTimeout
setTimeout(()=>{},2000);
为什么要使用Promise?
1. 支持链式调用,可以解决回调地狱问题
回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
asyncFunc1(opt,(...args1) => {
asyncFunc2(opt,(...args2) => {
asyncFunc3(opt,(...args3) => {
asyncFunc4(opt,(...args4) => {
// some operation
})
})
})
})
回调地狱的缺点:不便于阅读;不便于进行异常处理
2. 指定回调函数的方式更加灵活
- 传统的必须在启动异步任务前指定回调函数
- promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数(多个)
怎么用?
Promise初体验
<div class="app">
<h2>Promise 初体验</h2>
<button class="btn" id="btn">点击抽奖</button>
</div>
<script>
// 生成随机数
function rand(m,n){
return Math.ceil(Math.random() * (n-m+1)) + m - 1;
}
// 获取元素对象
const btn = document.querySelector("#btn");
// 绑定点击事件
btn.addEventListener('click',function(){
// // 定时器
// setTimeout(()=>{
// // 30%的中奖概率
// let n = rand(1,100);
// if (n <= 30) {
// alert("恭喜,10元");
// }else{
// alert('谢谢惠顾');
// }
// })
// Promise形式实现(构造函数:接收一个参数为函数)
// resolve 解决 函数类型的数据
// reject 拒绝 函数类型的数据
const p = new Promise((resolve,reject) => {
setTimeout(()=>{
// 30%的中奖概率
let n = rand(1,100);
if (n <= 30) {
resolve(n); // 将promise对象的状态设置为【成功】,并传递结果
}else{
reject(n) // 将promise对象的状态设置为【失败】
}
})
})
// 调用then方法
p.then((value) => {
// 为对象状态成功的回调
alert("恭喜,10元,号码为" + res);
},(reason) => {
// 为对象状态失败的回调
alert('谢谢惠顾,号码为' + res);
});
})
</script>
Promise实践练习
fs模块
// 普通写法
const fs = require('fs');
fs.readFile('E:\\test.txt',(err,data) => {
// 出错:直接抛出错误
if(err) throw err;
// 输出文件内容
console.log(data.toString());
})
// Promise写法
let p = new Promise((resolve,reject) => {
fs.readFile('E:\\test.txt',(err,data) => {
// 出错
if(err) reject(err);
//成功
resolve(data);
})
})
// 调用then
p.then(value => {
console.log(value.toString());
},reason => {
console.log(reason);
})
AJAX请求
<div class="app">
<button id="btn" class="btn">发送ajax请求</button>
</div>
<script>
// 获取元素接口
const btn = document.querySelector('#btn');
btn.addEventListener('click',function(){
// 创建Promise
const p = new Promise((resolve,reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化
xhr.open('get', 'https://api.apiopen.top/getJoke');
// 3. 发送
xhr.send();
// 4.处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 控制台输出响应体
resolve(xhr.response)
} else {
// 控制台输出响应码
reject(xhr.status)
}
}
}
});
// 调用then方法对成功和失败的结果进行处理
p.then(value => {
console.log(value)
},reason => {
console.warn(reason)
})
// 优点:1.指定回调函数更加灵活;2.支持链式调用,解决地狱回调问题
})
</script>
Promise封装练习
fs文件读取操作
/*
封装一个函数,mineReadFile 读取文件内容
参数: path 文件路径
返回: promise 对象
*/
function mineReadFile(path) {
return new Promise((resolve,reject) => {
// 读取文件
require('fs').readFile(path,(err,data) => {
if (err) reject(err);
// 成功
resolve(data);
})
})
}
// 不需要在原始的readFile中写回调函数
mineReadFile('E:\\test.txt')
.then(value=>{
// 输出文件内容
console.log(value.toString());
},reason => {
console.log(reason);
});
AJAX请求
/*
封装一个函数sendAJAX发送GET AJAX请求
参数 URL
返回结果 Promise对象
*/
function sendAJAX(url){
return new Promise((resolve,reject) => {
// XMLHttpRequest:通过HTTP在浏览器和web服务器之间收发XML或其它数据
const xhr = new XMLHttpRequest();
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)
}
}
}
})
}
sendAJAX('https://api.apiopen.top/getJoke')
.then(value => {
console.log(value)
},reason => {
console.warn(reason)
})
util.promisify(original)
一个错误优先的回调
/*
util.promisify 方法
*/
// 引入 util模块
const util = require('util');
// 引入fs模块
const fs = require('fs');
// 返回一个新函数
// 使用 util.promisify 函数将 fs.readFile 函数的回调方式转换为返回 Promise 的方式。
// 这意味着当你调用 mineReadFile 函数时,它将返回一个 Promise,而不是使用回调函数。
let mineReadFile = util.promisify(fs.readFile);
// 因为返回的是一个Promise,所以可以直接使用.then
mineReadFile('E:\\test.txt').then(value => {
console.log(value.toString());
})
Promise对象状态属性介绍
Promise的状态
实例对象中的一个属性 【PromiseState】
- pending 未决定的
- resolved / fulfilled 成功
- rejected 失败
Promise的对象的值
实例对象中的另一个属性 【PromiseResult】
对象【成功/失败】的结果
- resolve
- reject
1.pending变为resolved
2.pending变为rejected
Promise工作流程
Promise的API
Promise.all方法:(promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
let p1 = new Promise((resolve,reject) => {
resolve('ok');
})
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('ok'); // 只返回成功的结果
const result = Promise.all([p1,p2,p3])
console.log(result); // [[PromiseState]]:"fulfilled"
let p4 = Promise.reject('err'); // 只返回失败的结果
const result2 = Promise.all([p1, p2, p3,p4])
console.log(result2); // [[PromiseState]]:"rejected"
// [[PromiseResult]]: "err" 结果为失败的那个promise的值
Promise.race方法:(promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的结果状态就是最终的结果状态
let p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok');
},1000)
})
let p2 = Promise.resolve('success'); // 第一个完成
let p3 = Promise.resolve('okkkk');
const result = Promise.race([p1,p2,p3])
console.log(result); // [[PromiseResult]]: "success"
Promise的几个关键问题
1. 如何改变对象的状态
let p = new Promise((resolve,reject) => {
// 1. resolve函数
resolve("ok"); // pending => fulfilled (resolved)
// 2.reject 函数
reject("error"); // pending => rejected
// 3. 抛出错误
throw '出问题了'; // pending => rejected
});
console.log(p);
2. 能否指定多个回调
当promise改变为对应状态时都会调用
let p = new Promise((resolve,reject) => {
resolve("ok"); // 成功,以下指定的回调都会执行
// 如果将以上成功回调注释掉,promise状态为pending,以下回调不会执行。
});
// 指定回调1
p.then(value => {
console.log(value);
})
// 指定回调2
p.then(value => {
alert(value);
})
3. 改变Promise状态和指定回调函数谁先谁后
都有可能
// 1.resolve()改变状态先执行,then()后执行:当(resolve,reject) => {}执行器当中的任务是同步任务时,直接调用resolve("ok");
// 就是先改变promise的状态,然后再执行回调
let p1 = new Promise((resolve, reject) => {
resolve("ok");
});
// 2.then()先执行,resolve()改变状态后执行:当执行器当中的任务是异步任务时
let p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("ok");
},1000)
});
p.then(value => {
console.log(value);
},reason => {
})
什么时候才能得到数据?(回调函数什么时候执行)
- 先指定回调的情况下(使用异步编程):状态发生改变时,回调函数才会调用,得到数据
- 先改变状态,当指定回调(给实例身上绑事件)时,回调函数就会调用。
4.promise.then()返回的新promise的结果状态由什么决定?
- 如果抛出异常,新promise变为rejected,reason为抛出的异常
- 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
- 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
let p = new Promise((resolve, reject) => {
resolve("ok");
});
let result = p.then(value => {
// 以下的返回值决定result的结果
// console.log(value);
// // 抛出错误
// throw "error";
// // 2.返回结果为非promise类型的对象
// return 111;
// 3.返回结果是Promise对象
return new Promise((resolve,reject) => {
resolve('success')
})
},reason => {
console.warn(reason);
})
console.log(result);
5.promise如何串连多个操作任务?
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
},1000)
});
p.then(value => {
return new Promise((resolve,reject) => {
resolve("success");
});
}).then(value => {
// then的返回结果为promise,状态由它指定的回调函数的返回值决定
// 此回调函数的返回值没写,为undefined
console.log(value); // success
}).then(value => {
// 输出上面回调的结果
console.log(value); // undefined
})
6.promise异常穿透
什么是异常穿透:当使用promise的then进行链式调用时,可以在最后指定失败的回调,前面的任何操作出了异常,都会传到最后失败的回调中处理
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
},1000)
});
p.then(value => {
throw '失败了!';
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
// 只需要在最后指定一个失败的回调,就可以处理错误的结果
console.log(reason); // 输出'失败了'
})
7.如何中断promise链
返回pending状态的promise对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
},1000)
});
p.then(value => {
console.log(111);
// 有且只有一种方式
return new Promise(() => {}) // pengding:状态未改变,以下回调无法执行
}).then(value => {
console.log(222);
}).catch(reason => {
console.log(reason);
})
Promise自定义封装
1.初始结构搭建
2.resolve和reject结构搭建及代码实现
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
// resolve('ok');
reject('error');
})
console.log(p)
</script>
<promise.js>
// 声明构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 保存实例对象的 this 值
const self = this;
// resolve函数
function resolve(data){
// 1. 修改对象的状态(promiseState)
// 这里的this改变的不是实例对象,而是window对象
// this.PromiseState = 'fulfilled'
self.PromiseState = 'fulfilled';
//2. 设置对象结果值(promiseResult)
self.PromiseResult = data
}
// reject函数
function reject(data){
// 1. 修改对象的状态(promiseState)
self.PromiseState = 'rejected';
//2. 设置对象结果值(promiseResult)
self.PromiseResult = data
}
// 同步调用【执行器函数】
executor(resolve,reject)
}
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
}
3.throw抛出异常改变状态
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
throw "error";
})
console.log(p)
</script>
<promise.js>
// 在构造函数中修改如下代码
// throw抛出异常的情况下调用
try {
// 同步调用【执行器函数】
executor(resolve,reject)
} catch (error) {
// 修改promise对象状态为【失败】
reject(); // 直接调用reject()函数
}
4.确保promise状态只更改一次
在resolve和reject函数中判断状态,不为pending时直接返回
<promise.js>
// resolve函数
function resolve(data){
// 判断状态
if(self.PromiseState !== 'pending') return;
self.PromiseState = 'fulfilled';
self.PromiseResult = data
}
// reject函数
function reject(data){
// 判断状态
if(self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data
}
5.then方法中执行回调
<promise.js>
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
// 调用回调函数 判断promiseState
if(this.PromiseState === 'fulfilled'){
// 把结果往回传
onResolved(this.PromiseResult);
}
if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult);
}
}
6.异步任务回调的执行
问题:以下成功的回调不会执行,代码从上往下走setTimeout还未执行(需等待一秒),同步代码不会等待setTimeout执行,会先调用p.then,但到达then方法时p的状态仍处在pending,promise.js中的then方法中没有对pending状态的判断,所以没有返回。
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok')
},1000)
})
p.then(value => {
console.log(value); // 不会执行
},reason => {
console.log(reason);
})
</script>
解决:在构造函数中添加一个对象属性(callback),用于存储回调函数,promise的then方法添加判断状态是否为pending,如果是就将回调函数添加到callback对象中,添加成功后,就可以在resolve函数中执行成功的回调,失败的回调同理。
<promise.js>
// 声明构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明属性
this.callback = {};
// 保存实例对象的 this 值
const self = this;
// resolve函数
function resolve(data){
// 判断状态
if(self.PromiseState !== 'pending') return;
// 1. 修改对象的状态(promiseState)
self.PromiseState = 'fulfilled';
//2. 设置对象结果值(promiseResult)
self.PromiseResult = data
// 调用成功的回调函数
if(self.callback.onResolved){ // 如果回调当中有onResolved属性就执行它
self.callback.onResolved(data); // 参数别忘了
}
}
// reject函数
function reject(data){
// 判断状态
if(self.PromiseState !== 'pending') return;
// 1. 修改对象的状态(promiseState)
self.PromiseState = 'rejected';
//2. 设置对象结果值(promiseResult)
self.PromiseResult = data
// 调用失败的回调函数
if(self.callback.onRejected){ // 如果回调当中有onResolved属性就执行它
self.callback.onRejected(data); // 参数别忘了
}
}
// throw抛出异常的情况下调用
try {
// 同步调用【执行器函数】
executor(resolve,reject)
} catch (error) {
// 修改promise对象状态为【失败】
reject(); // 直接调用reject()函数
}
}
// 添加then方法
Promise.prototype.then = function(onResolved,onRejected){
// 调用回调函数 判断promiseState
if(this.PromiseState === 'fulfilled'){
// 把结果往回传
// onResolved(); // 不传结果为undefined
onResolved(this.PromiseResult);
}
if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult);
}
// 判断pending状态
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callback = {
onResolved : onResolved,
onRejected : onRejected
}
}
}
7.指定多个回调的实现
例如如下代码,控制台不会输出东西,但会有弹框,回调函数被覆盖
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok')
},1000)
})
p.then(value => {
console.log(value);
},reason => {
console.log(reason);
})
p.then(value => {
alert(value);
},reason =>{
alert(reason);
})
</script>
解决:保存所有回调
<promise.js>
// 1.将callback对象改为callbacks数组
this.callbacks = [];
// 2.将回调函数push到callbacks数组中
this.callbacks.push({
onResolved : onResolved,
onRejected : onRejected
})
// 3. 遍历执行成功回调函数(失败同理)
self.callbacks.forEach(item => {
item.onResolved(data);
});
8.同步任务then返回结果
首先,p.then方法的返回结果应该是一个promise对象,以下代码返回undefined
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
resolve('ok')
})
const result = p.then(value => {
console.log(value);
},reason => {
console.log(reason);
})
console.log(result)
</script>
既然如此,就返回一个promise对象,但状态一直处在pending
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
resolve('ok')
})
const result = p.then(value => {
return 'hello';
},reason => {
console.log(reason);
})
console.log(result)
</script>
<promise.js>
Promise.prototype.then = function(onResolved,onRejected){
return new Promise((resolve,reject) => {
// 调用回调函数 判断promiseState
if(this.PromiseState === 'fulfilled'){
// 把结果往回传
// onResolved(); // 不传结果为undefined
onResolved(this.PromiseResult);
}
if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult);
}
// 判断pending状态
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callbacks.push({
onResolved : onResolved,
onRejected : onRejected
})
}
})
}
需要根据回调函数的执行结果改变promise对象的状态,以及抛出异常的情况下返回的结果
<promise.js>
Promise.prototype.then = function(onResolved,onRejected){
return new Promise((resolve,reject) => {
// 调用回调函数 判断promiseState
if(this.PromiseState === 'fulfilled'){
try {
// 获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
// 判断
if(result instanceof Promise){
// 如果是promise类型对象
result.then(v => {
resolve(v);
},r =>{
reject(v);
})
}else{
// 结果的对象状态为 成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult);
}
// 判断pending状态
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callbacks.push({
onResolved : onResolved,
onRejected : onRejected
})
}
})
}
9. 异步任务then返回结果
以下返回的状态为pending
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok')
},1000)
})
// 由于是异步任务,代码走到这里时promise为pending
const res = p.then(value => {
console.log(value);
},reason => {
console.warn(reason);
})
console.log(res)
</script>
所以promise.js中的代码会来到这里
需要对里面的代码进行改进
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callbacks.push({
onResolved : function(){
// 执行成功回调函数
let result = onResolved(self.PromiseResult)
// 判断
if(result instanceof Promise){
result.then(v => {
resolve(v);
}, r=> {
reject(r);
})
}else{
resolve(result);
}
},
onRejected : function(){
// 执行失败回调函数
let result = onRejected(self.PromiseResult)
// 判断
if(result instanceof Promise){
result.then(v => {
resolve(v);
}, r=> {
reject(r);
})
}else{
resolve(result);
}
}
})
}
这个要注意的是当setTimeout里面执行的是reject时,PromiseState仍为"fulfilled",这个因为没有return返回的就是undefined,而undefined属于非promise,所以是成功的状态。
加上try catch解决抛出异常的问题
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('error')
},1000)
})
// 由于是异步任务,代码走到这里时promise为pending
const res = p.then(value => {
return 'oh'
},reason => {
// console.warn(reason);
throw "Error"
})
console.log(res)
</script>
<promise.js>
// 判断pending状态
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callbacks.push({
onResolved : function(){
try{
// 执行成功回调函数
let result = onResolved(self.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(self.PromiseResult)
// 判断
if(result instanceof Promise){
result.then(v => {
resolve(v);
}, r=> {
reject(r);
})
}else{
resolve(result);
}
} catch (e) {
reject(e);
}
}
})
}
10. then方法完善与优化
同步任务调用reject回调情况下
if(this.PromiseState === 'rejected'){
try {
let result = onRejected(this.PromiseResult);
if(result instanceof Promise){
result.then(v => {
resolve(v);
},r => {
reject(r);
})
}else{
resolve(result);
}
} catch (e) {
reject(e);
}
}
以下重复代码太多,只有onRejected和onResolved的区别
try {
let result = onRejected(this.PromiseResult);
if(result instanceof Promise){
result.then(v => {
resolve(v);
},r => {
reject(r);
})
}else{
resolve(result);
}
} catch (e) {
reject(e);
}
封装callback函数
Promise.prototype.then = function(onResolved,onRejected){
const self = this;
return new Promise((resolve,reject) => {
function callback(type){
try {
// 获取回调函数的执行结果,这里要改变this的指向
let result = type(self.PromiseResult);
// 判断
if(result instanceof Promise){
// 如果是promise类型对象
result.then(v => {
resolve(v);
},r =>{
reject(v);
})
}else{
// 结果的对象状态为 成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 调用回调函数 判断promiseState
if(this.PromiseState === 'fulfilled'){
callback(onResolved);
}
if(this.PromiseState === 'rejected'){
callback(onRejected);
}
// 判断pending状态
if(this.PromiseState === 'pending'){
// 保存回调函数(保存在任务队列中,或叫微任务队列)
// 将回调函数绑定在实例上
this.callbacks.push({
onResolved : function(){
callback(onResolved);
},
onRejected : function(){
callback(onRejected);
}
})
}
})
}
11.封装catch方法和异常穿透
指定失败的回调函数
<promise.html>
<script src="./Promise.js"></script>
<script>
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('err')
},1000)
})
p.then(value => {
console.log(111);
throw "Error";
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
})
</script>
promise.js添加处理错误的函数,在then中已经有处理错误的方法,直接调用
// 添加catch方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected);
}
但直接使用以上catch方法会报“onRejected is not a function”的错误,原因是then后面只传递了成功的回调,失败的回调没有传递,需要在then方法接收参数后判断onRejected是否为function,不是则初始化。解决异常穿透的问题。
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason;
}
}
这样一来,then后面只传递成功的回调就不会报错,但then既不传递成功的回调时依然会报错,需要添加onResolved的判断
if(typeof onResolved !== 'function'){
onResolved = value => value
}
12.Promise.resolve方法封装
直接调用以下代码会报“Promise.resolve is not a function”。(非自带Promise)
promise.html
<script src="./Promise.js"></script>
let p = Promise.resolve("ok");
console.log(p)
promise.js添加resolve方法
// 添加 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);
}
})
}
promise.html
let p = Promise.resolve("ok");
let p2 = Promise.resolve(new Promise((resolve,reject) => {
resolve("success")
}))
// 返回的promise的结果成功p2也成功
console.log(p2) // fulfilled,success
13.Promise.reject方法封装
<promise.html>
<script src="./Promise.js"></script>
let p = Promise.resolve("ok");
let p2 = Promise.reject(new Promise((resolve,reject) => {
resolve("success")
}))
// reject方法不管传入什么类型的值结果都是失败的
console.log(p2) // rejected,Promise
<promise.js>
// 添加 reject 方法
Promise.resolve = function(reason){
// 返回Promise对象
return new Promise((resolve,reject) => {
reject(reason);
})
}
14.Promise.all方法封装
<promise.html>
<script src="./Promise.js"></script>
let p1 = Promise.resolve((resolve, reject) => {
setTimeout(()=>{
resolve("success")
},1000)
});
let p2 = Promise.reject("yes")
let p3 = Promise.resolve("okkk")
let result = Promise.all([p1,p2,p3])
console.log(result)
<promise.js>
// 添加 all 方法
Promise.all = function(promises){
return new Promise((resolve,reject) => {
// 声明变量
let count = 0; // 统计resolve的promise个数
let arr = []; // 存储执行成功的返回结果
// 遍历数组
for(let i=0;i<promises.length;i++){
// 执行每一个promise
promises[i].then(v => {
// 能进到这里的对象状态都是成功的
count++;
// 将当前对象成功的结果放入arr中,push()不能保证结果按顺序存放
arr[i] = v;
// 判断:全部的结果都是成功才能改变状态
if(count === promises.length){
resolve(arr);
}
},r => {
// 只要有一个执行失败就直接返回
reject(r);
})
}
})
}
15.Promise.race方法封装
<promise.html>
<script src="./Promise.js"></script>
let p1 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("success")
},1000)
});
let p2 = Promise.resolve("yes")
let p3 = Promise.resolve("okkk")
let result = Promise.race([p1,p2,p3])
console.log(result) // yes
<promise.js>
// 添加 race 方法
Promise.race = function(promises){
return new Promise((resolve,reject) => {
// 遍历数组
for(let i=0;i<promises.length;i++){
// 执行每一个promise
console.log("11111",promises[i]);
promises[i].then(v => {
resolve(v)
},r => {
reject(r);
})
}
})
}
16.then方法回调的异步执行
let p = new Promise((resolve, reject) => {
resolve("success");
console.log(111);
});
// then方法里面的函数是异步微任务
p.then((result) => {
console.log(222)
});
console.log(333)
以上代码正确执行顺序是111 333 222
但使用自定义封装的promise的执行顺序是111 222 333
我们需要对then方法做一些改进:在then方法中把调用回调函数放入异步任务中
async & await
async
- 函数的返回值为promise对象
<script>
async function main(){};
let result = main();
console.log(result); // Promise
</script>
- promise对象的结果由async函数执行的返回值决定
async function main(){
// 1.返回值为非promise对象:promise的状态为fulfilled,结果为123
// return 123;
// 2.返回值是一个Promise对象:由promise对象的结果决定main()的结果
// return new Promise((resolve,reject) => {
// resolve('ok');
// })
// 3.抛出异常:promise的状态为失败,结果为a oh
throw "a oh"
};
let result = main();
console.log(result);
await
- await右侧的表达式一般为promise对象,也可以是其它值
- 表达式为promise对象时,await返回的是promise成功的值,promise失败时需要通过try...catch..捕获处理
- 表达式为其它值时,直接将此值作为await的返回值
async function main(){
let p = new Promise((resolve,reject) => {
resolve('ok');
})
let p2 = new Promise((resolve, reject) => {
reject('error');
})
// 1.右侧为promise的情况
let res = await p;
console.log(res); // ok
// 2.右侧为其它数据的情况
let res2 = await 20;
console.log(res2); // 20
// 3.promise是失败的状态
try {
let res3 = await p2;
} catch (e) {
console.log(e); // error
}
};
main()
async和await结合实践:拼接三个文件的内容
普通回调函数写法
const fs = require('fs')
fs.readFile('1.txt',(err,data1) => {
if(err) throw err;
fs.readFile('2.txt',(err,data2) => {
if(err) throw err;
fs.readFile('3.txt',(err,data3) => {
if(err) throw err;
console.log(data1+data2+data3);
})
})
})
async await写法:看不到回调函数,使代码看起来更简洁
const fs = require('fs')
const util = require('util');
const mineReadFile = util.promisify(fs.readFile); //将api转成promise型对象
async function main(){
try {
let data1 = await mineReadFile('./1.txt');
let data2 = await mineReadFile('./1.txt');
let data3 = await mineReadFile('./1.txt');
console.log(data1 + data2 +data3);
} catch (e) {
console.log(e);
}
}
main()
async与await结合发送AJAX请求
<button id="btn">点击获取信息</button>
<script>
function sendAJAX(url) {
return new Promise((resolve, reject) => {
// XMLHttpRequest:通过HTTP在浏览器和web服务器之间收发XML或其它数据
const xhr = new XMLHttpRequest();
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)
}
}
}
})
}
// 接口地址:http://localhost:3003/player/getPlayer2
let btn = document.querySelector('#btn')
btn.addEventListener('click',async function(){
// 获取信息
let mess = await sendAJAX('http://localhost:3003/player/getPlayer2');
console.log(mess);
})
</script>