promise是一个容器,保存着某个未来才会结束的事件(异步操作)的结果。
promise有三种状态:pending(进行中)、fulfilled(成功)和reject(失败),只有异步操作的结果可以改变promise的状态。一旦promise从pending状态转化为fulfilled或reject后就不会再改变。
Promise在新建后就会立即执行,Promise.then()方法会在当前脚本所有同步任务执行完后才会执行
const promise = new Promise(function(resolve, reject) {
//...异步操作
if (/* 异步操作成功 */){
resolve(value); // resolve将Promise的状态由pending变为resolved
} else {
reject(error); // reject将Promise的状态由pending变为失败
}
});
promise.then(function(value) {
// 异步操作执行成功后的处理
}, function(error) {
// 异步操作失败的处理
});
如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数。
resolve
函数的参数除了正常的值以外,还可能是另一个 Promise 实例
p1
和p2
都是 Promise 的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行
const p1 = new Promise(function (resolve, reject) {
//三秒后返回reject
setTimeout(() => reject(new Error('fail')), 3000) //reject参数为错误信息ERROR
})
const p2 = new Promise(function (resolve, reject) {
//p1的状态决定了p2的状态,后续语句便针对p1的状态
setTimeout(() => resolve(p1), 1000)
})
//由于p1返回reject,p1又决定了p2的状态,因此触发了.catch 打印error
p2
.then(result => console.log(result))
.catch(error => console.log(error))
then
方法返回的是一个新的Promise对象,因此可以采用链式写法。使用链式的写法, 可以指定一组按照次序的回调函数。
axios.get('“异步请求”')
.then(function(reslut) {
//获取返回结果reslut
return reslut
})
.then(function(reslut) {
// 执行得到返回结果后相应的操作
});
catch
方法用于指定发生错误(reject)时的回调函数
let promise = new Promise(
function(resolve, reject) {
throw new Error('test'); //reject
}
);
promise
.then(function(){
//正常情况的回调resolve
})
.catch(function(){
//异常情况的回调reject:test
})
无论Promise最后状态如何都会执行的方法,在ES2018引入。
finally()不接受任何参数,在finally里的方法与状态无关,finally本质上是then的特例。如果不使用finally,相当于在then和catch的方法里将finally的方法各写一次。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
将多个Promise实例包装为一个新的Promise实例。
Promise.all()接受一个数组作为参数,p1,p2, p3都是Promise实例。
const p = Promise.all([p1, p2, p3])
p的状态由p1,p2,p3决定,只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
const databasePromise = connectDatabase();
const booksPromise = databasePromise
.then(findAllBooks);
const userPromise = databasePromise
.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));
const p = Promise.race([p1, p2, p3]);
Race()与all方法参数一致,但是race只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
将现有对象转化为Promise对象
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成的deferred
对象,转为一个新的 Promise 对象。
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve
将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable
对象
Promise.resolve
方法会将这个对象转为 Promise 对象,然后就立即执行thenable
对象的then
方法。
(3)参数不是具有then
方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then
方法的对象,则Promise.resolve
方法返回一个新的 Promise 对象,状态为resolved
。
(4)不带有任何参数
Promise.resolve()
方法允许调用时不带参数,直接返回一个resolved
状态的 Promise 对象。
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
上面代码生成一个 Promise 对象的实例p
,状态为rejected
,回调函数会立即执行。
注意,Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// true
上面代码中,Promise.reject
方法的参数是一个thenable
对象,执行以后,后面catch
方法的参数不是reject
抛出的“出错了”这个字符串,而是thenable
对象。
================================================================
在JavaScript中需要在数组中循环调用异步方法,又需要等待上一轮异步方法的执行结果才能继续,因此我们需要使用Promise来完成调用异步方法的顺序执行。
let usePromise = async()=>{
let result = [], array = [1,2,3,4] // array作为循环遍历用
let pro
//开始循环遍历
array.forEach(element=>{
pro = new Promise(resolve=>{
//setTimeout代表异步方法,等待一秒后执行
setTimeout(()=>{
result.push(element);
resolve();
}, 1000);
})
})
await pro;
console.log(result); // [1,2,3,4]
}
let array1 = [1]
function prom1 (){
return new Promise(function(resovle){
setTimeout(()=>{
array1.push('a');
resovle(array1);
}, 1000);
})
}
function prom2 (){
return new Promise(function(resovle){
setTimeout(()=>{
array1.push('b');
resovle(array1);
}, 1000);
})
}
function prom3 (){
return new Promise(function(resovle){
console.log(array1)
})
}
let pro = new Promise(function(resolve, reject){
resolve();
})
pro.then(prom1)
.then(prom2)
.then(prom3) // [1, a, b]
以下代码功能: 获取一个URL数组,通过for循环依次访问数组中的各个URL地址,进入各个URL页面的子页面获取数据,当一个页面的所有数据都获取到并处理完毕,才会进入下一个for循环的异步请求访问新的URL页面请求数据。
let array =[1,2,3,4];
let p = Promise.resolve();
for (let i = 0; i < array.length; i ++){
p = p.then(_=>new Promise(resolve=>{
//异步方法,发送http的GET请求获取数据
superagent.get(bookUrl[i]).end(async(err, res) => {
if (err) {
console.log("err",err);
return
} else {
let $ = cheerio.load(res.text);
bookName.push($('div.book-info').find('h1').find('em').text())
let readURL = $('div.book-info').find('a#readBtn').attr('href');
//异步方法,获取数据中的请求路由
superagent.get("https:"+readURL).end((err, res)=>{
if(err){
console.log(err);
//reject(); 使用reject需要在new Promise传入该参数,并有.catch方法
}else{
let $ = cheerio.load(res.text);
content.push($('div.read-content').find('p').text());
if($('div.read-content').find('p').text()!=="") {
// 当resolve时此次方法调用才算完成,才会进入下次循环发送交易
resolve(content)
}
}
})
}
})
}))
}
p.then(function(){
doSomething();
})
=============================>>>
//大体框架
let p = Promise.resolve();
array.forEach((value,index, array)=>{
p= p.then(_=>{new Promise(resolve)=>{
doSomething({
...
resolve();
})
}})
})
p.then(function(){
doSomething...
})