介绍
es6引入的,进行异步编程的新的解决方案,语法上来说是构造函数,可以封装异步任务,并且对结果进行处理 Promise最大的好处在于支持链式调用,可以解决回掉地狱的问题,在指定回掉和错误处理这块更加的灵活和方便。
Promise介绍与基本使用
- 抽象表达
- es6引入的
- 进行异步编程的新的解决方案,旧的解决方案是使用回掉函数
- 具体表达
- 语法上来说是构造函数
- 可以封装异步任务,并且可以获取成功/失败的结果值
异步编程
- fs文件操作(fs是nodeJs使用)
require(`js`).readFile('./index.html', (err, data)=>{})
- 数据库操作
- AJAX
- 定时器
setTimeout(()=>{}, 2000)
回调地狱
asyncFunc1(opt, (...args1) => {
asyncFunc2(opt, (...args2) => {
asyncFunc3(opt, (...args3) => {
asyncFunc4(opt, (...args4) => {
//some operation
})
})
})
})
Promise对象状态属性
1. 实例对象中的一个属性 『PromiseState』 具有以下三种状态
- pending 未决定
- resolved / fullfilled 成功
- rejected 失败
2. promise 的状态改变
- pending 变为 resolved
- pending 变为 rejected
Promise对象的值
实例对象种的另一个属性 **PromiseResult** 保存着异步任务·对象 成功/失败 的结果
- resolve
- reject
Promise的基本流程
Promise的基本使用
- Promise封装AJAX
<script>
/**
* 封装一个函数sendAJAX发送GET AJAX请求
* 参数 URL
* 返回结果 Promise 对象
*/
function sendAJAX(url){
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response)
} else {
resolve(xhr.status)
}
}
}
})
}
sendAJAX('https://api.apiopen.top/getJoke')
.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
})
</script>
- Promise封装文件读取fs
//封装一个函数,读取文件内容
//参数:path 文件路径
//返回Promise对象
function mineReadFile(path){
return new Promise((resolve, reject) => {
require('fs').readFile(path, (err, data) => {
if(err) reject(err);
resolve(data);
})
})
}
mineReadFile('./resource/content.txt')
.then(value => {
console.log(value.toString());
}, reason => {
console.log(reason);
})
3util.promisify方法
可以将函数直接变成promise的封装方式,不用再去手动封装
const util = require('util')
const fs = require('fs')
let mineReadFile = util.promisify(fs.readFile)
mineReadFile('./resource/content.txt')
.then(value => {
console.log(value.toString());
}, reason => {
console.log(reason);
})
Promis中常用API概述
1.Promise构造函数:Promise(excutor){}
(1) executor 函数: 执行器 (resolve, reject) => {}
(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}
(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}
<script>
///打印顺序 111,222
//说明: executor 会在 Promise 内部立即`同步调用`,异步操作在执行器中执行,换话说Promise支持同步也支持异步操作
let p = new Promise((resolve, reject) => {
console.log(111);//同步调用的,很重要
})
console.log(222);
</script>
2.Promise.prototype.then 方法: (onResolved, onRejected) => {}
(1) onResolved 函数: 成功的回调函数 (value) => {}
(2) onRejected 函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调 返回一个新的 promise 对象
3.Promise.prototype.catch 方法: (onRejected) => {}
(1) onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
(2) 异常穿透使用:当运行到最后,没被处理的所有异常错误都会进入这个方法的回调函数中
4. Promise.resolve 和 Promise.reject方法
<script>
//
//如果传入的参数为 非Promise类型的对象(数字。。。)。则返回的结果为成功promise对象
//如果传入的参数为 Promise类型的对象(数字。。。)。则参数的结果决定了resolve的结果
let p1 = Promise.resolve(521);
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('ok')
reject('err')
}))
console.log(p1);
console.log(p2);
p2.catch(reason => {
console.log(reason);
})
let p3 = Promise.reject(521);
console.log(p3);
let p4 = Promise.reject(new Promise((resolve, reject) => {
resolve('ok')
// reject('err')
}))
console.log('p4', p4);
</script>
5.Promise.all方法
说明: 返回一个新的 promise, 只有所有的 promise
都成功才成功, 只要有一 个失败了就直接失败
<script>
let p1 = new Promise((resolve, reject) => {
resolve('ok')
})
// let p2 = Promise.resolve('Success')
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('Oh Yeah')
const result = Promise.all([p1, p2, p3])
console.log(result);
</script>
6.Promise.race方法
由第一个改变状态的promise对象的结果决定
<script>
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000)
})
// let p2 = Promise.resolve('Success')
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('Oh Yeah')
const result = Promise.race([p1, p2, p3])
console.log(result);
</script>
Promise的几个关键问题
Ⅰ-如何改变 promise 的状态?
(1) resolve(value): 如果当前是 pending 就会变为 resolved
(2) reject(reason): 如果当前是 pending 就会变为 rejected
(3) 抛出异常: 如果当前是 pending 就会变为 rejected
<script>
let p = new Promise((resolve, reject) => {
//1. resolve函数 pending =》 resolved(fulfilled)
// resolve('ok')
//2. resolve函数 pending =》 rejected
// resolve('error')
//3.抛出错误
throw '出问题了'
});
console.log(p);
</script>
Ⅱ-一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise
改变为对应状态时都会调用,改变状态后,多个回调函数都会调用,并不会自动停止
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
});
p.then(value => {
console.log(value);
})
p.then(value => {
alert(value)
})
</script>
改变promise状态和指定回调函数,谁先执行?
(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
先指定回调再改变状态(异步):先指定回调--> 再改变状态 -->改变状态后才进入异步队列执行回调函数
先改状态再指定回调(同步):改变状态 -->指定回调 并马上执行回调
(2) 如何先改状态再指定回调? -->注意:指定并不是执行
① 在执行器中直接调用 resolve()/reject() -->即,不使用定时器等方法,执行器内直接同步操作
② 延迟更长时间才调用 then() -->即,在.then()这个方法外再包一层例如延时器这种方法
(3) 什么时候才能得到数据?
① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
<script>
//指定回调:在then中放入成功或者失败状态,对应状态有特定的回调函数,对应关系,但没有实施
let p = new Promise((resolve, reject) => {
// resolve('ok') //1 同步任务
setTimeout(() => {
resolve('ok') //3 异步任务
}, 1000);
});
//2
p.then((result) => {
console.log(result);
}).catch((err) => {
});
</script>
promise.then()返回的新 promise 的结果状态由什么决定?
(1) 简单表达: 由 then()指定的回调函数执行的结果决定
(2) 详细表达:
① 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
② 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
③ 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
console.log(value);
// 1. 抛出错误 ,变为 rejected
throw '出了问题';
// 2. 返回结果是非 Promise 类型的对象,新 promise 变为 resolved
return 521;
// 3. 返回结果是 Promise 对象,此 promise 的结果就会成为新 promise 的结果
return new Promise((resolve, reject) => {
// resolve('success');
reject('error');
});
}, reason => {
console.warn(reason);
});
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
console.log(value);
// 1. 抛出错误 ,变为 rejected
throw '出了问题';
// 2. 返回结果是非 Promise 类型的对象,新 promise 变为 resolved
return 521;
// 3. 返回结果是 Promise 对象,此 promise 的结果就会成为新 promise 的结果
return new Promise((resolve, reject) => {
// resolve('success');
reject('error');
});
}, reason => {
console.warn(reason);
});
Promise如何串联多个操作任务
- promise的then()返回一个新的promise,可以变成then的链式调用
- 通过then的链式调用串联多个同步/异步任务
<script>
let p = new Promise((resolve, reject) => { setTimeout(() => {resolve('OK'); }, 1000); });
p.then(value => {return new Promise((resolve, reject) => { resolve("success"); });})
.then(value => {console.log(value);})
.then(value => { console.log(value);})
</script>
Ⅵ-promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
getJSON('./hong.json') .then(function(posts) { throw new Error('抛出异常') }) .then(res=>console.log(res),e=>console.log('被then的错误回调捕获',e) ) .catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('错误捕获: ', error); }); //执行结果: 被then的错误回调捕获 Error: 抛出异常 /******************** 利用异常穿透 ****************************************/ getJSON('./hong.json') .then(function(posts) { throw new Error('抛出异常') }) .then(res=>console.log(res) ) //此处差异,不指定 reject 回调,利用异常穿透传到最后 .catch(function(error) { console.log('错误捕获: ', error); }); //执行结果: 错误捕获: Error: 抛出异常注:可以在每个then()的第二个回调函数中进行err处理,也可以利用异常穿透特性,到最后用
catch去承接统一处理,两者一起用时,前者会生效(因为err已经将其处理,就不会再往下穿透)而走不到后面的catch
Ⅶ- 中断 promise 链?
在
关键问题2中,可以得知,当promise状态改变时,他的链式调用都会生效,那如果我们有这个一个实际需求:我们有5个then(),但其中有条件判断,如当我符合或者不符合第三个then条件时,要直接中断链式调用,不再走下面的then,该如何?(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个
pendding状态的promise 对象
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise(() => {});
})//有且只有这一个方式
.then(value => {
console.log(222);
})
.then(value => {
console.log(333);
})
.catch(reason => {
console.warn(reason);
});
</script>
async和await
async
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
await
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值
- 如果表达式是其它值, 直接将此值作为 await 的返回值