问题:
一个页面 进入页面会有四个接口请求,要求每个请求都带loading,请求完 loading消失。从页面加载速度,和资源浪费 两方面考虑, 用promise 如何实现
内容:
1、promise的含义
2、基本用法
3、 prototype.then()
4、prototype.catch()
5、Promise.reslove()
6、Promise.reject()
7、prototype.finally()
8、Promise.all()
9、Promise.any()
10、Promise.allSettled()
11、Promise.race()
12、Promise.try()
13、应用
一、promise的含义
自我定义:处理异步的一个手段,将异步操作改成同步操作的形式
标准定义:
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
特点:
三种状态: reslove(成功) reject(失败) pending(初始化/进行中)
状态只能是从pending ===>reslove/reject 并且状态已经改变,不会在改了
eg.
const promise = new Promise(function(resolve, reject) {
resolve('ok');
reject('no');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
缺点:
1、无法被取消,创建就会立即执行,中途不会取消,
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
2、如果不设置回调函数Promise内部的错误不会反应到外部,可以把内部错误给‘吃掉’
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});};
someAsyncThing().then(function() {
console.log('everything is great');});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
3、 当处于pending状态时,无法得知目前进展到哪一个阶段
二、基本用法
function fn() {
return new Promise((resolve, reject) => {
if(true) {
resolve(true)
}else {
reject(false)
}
})
}
fn.then(res => {
//成功
}).catch(oError => {
//失败
})
fn.then(res => {
//成功
},oError => {
//失败
})
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
三、prototype.then()
Promise 实例添加状态改变时的回调函数,then(reslove,reject),两个参数,第二个可以不写
四、prototype.catch()
用于指定发生错误时的回调函数 catch其实是对应then的第二个参数 then('',catch)
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}});
promise.catch(function(error) {
console.log(error);});
// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));});
promise.catch(function(error) {
console.log(error);});
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
function p1() {
return new Promise((resolve, reject) => {
resolve(111)
})
}
function p2() {
return new Promise((resolve, reject) => {
reject(2222222)
})
}
function p3() {
return new Promise((resolve, reject) => {
resolve(3333)
})
}
p1().then(p2).then(p3).then(res => {
console.log(res);
}).catch(e => {
console.log(e)
})
p1().then(function () {
p2().then(function () {
p3().then(res=> {
console.log(res);
}).catch(e=> {
console.log(e);
})
}).catch(e=> {
console.log(e);
})
})
五、Promise.reslove() 转化成Promise对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
1、如果参数是Promise对象 ,不做任何处理 直接返回
2、参数是具有then方法的对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}};
会将他转化成Promise对象,然后立即执行对象的then方法
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
let thenable = {
then: function(resolve, reject) {
reject(42);
}
};
let p1 = Promise.resolve(thenable);
console.log(p1);
p1.then(function(value) {
console.log(value);
}, oError=> {
console.log(oError); //42
});
3、参数没有then方法或者不是对象 ------ 返回一个新的 Promise 对象,状态为resolved
let p = Promise.resolve('hello')
console.log(p);
p.then(res=>{
console.log(res);
})
4、参数为空 ---- 会直接返回一个Promise对象
const p = Promise.resolve();
p.then(function () {
// ...
});
then是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时
setTimeout(function () {
console.log('three');
}, 0)
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
六、Promise.reject()
方法也会返回一个新的 Promise 实例,该实例的状态为rejected。 原封不动地作为reject的理由,变成后续方法的参数
const thenable = {
then(resolve, reject) {
reject('出错了');
}};
Promise.reject(thenable).catch(e => {
console.log(e === thenable)})
// true
七、prototype.finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
});
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);};
八、Promise.all() 都成功才成功,一个失败就失败
const p = Promise.all([p1, p2, p3]);
如果参数不是Promise对象会先变成Promise对象 通过Promise.reslove()方法
Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口 可循环的数据 for ... of
Array
Object
Map([['one', 1], ['two', 2]])
Set([1, 1, 2, 2, 1])
let p1 = 'hello'
let p2 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve(222)
},3100)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve(333)
},3000)
})
Promise.all(\[p1,p2,p3\]).then(res=> {
console.log(res);
},error => {
console.log(error);
})
//[
//'hello',
//222,
// 333
//]
//222
注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
那如果自己定义了then呢?
const p1 = new Promise((resolve, reject) => {
resolve('hello');})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
上面代码中,p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
九、Promise.any() 一个成功就成功,都失败才失败
目前还是提案中
npm install p-any
let p2 = new Promise((resolve, reject) => {
reject(222)
})
let p3 = new Promise((resolve, reject) => {
reject(333)
})
py([p2,p3]).then(res=> {
console.log(res);
},error => {
console.dir(error);
})
十、Promise.allSettled() 都结束才结束
如果参数不是Promise对象会先变成Promise对象 通过Promise.reslove()方法
let p1 = new Promise((resolve, reject) => {
resolve(111)
})
let p2 = new Promise((resolve, reject) => {
reject(222)
})
let p3 = new Promise((resolve, reject) => {
reject(333)
}
Promise.allSettled([p1,p2,p3]).then(res => {
console.log(res);
})
返回的是数组,reslove的是status和value, 失败的是status和reason
res.filter(ites=>ites.status==='fulfilled')
[
{
status: 'fulfilled',
value: 111
}
]
应用场景 : 不关心异步操作的结果,只关心这些操作有没有结束。例如loading结束
十一、Promise.race() 一个变就会变
const p = Promise.race([p1, p2, p3]);
如果参数不是Promise对象会先变成Promise对象 通过Promise.reslove()方法
那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
let p1 = 'hello'
let p2 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve(222)
},3100)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(function () {
reject(333)
},3000)
})
Promise.race(\[p1,p2,p3\]).then(res=> {
console.log(res);
},error => {
console.log(error);
})
应用场景: timeout
const p = Promise.race([
axios('/api'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request ****timeout')), 5000)
})]);
十二、Promise.try()
不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。
不必理会函数本身是同步的还是异步的,
let p4 =() => new Promise((resolve, reject) => {
reject(222)
})
let aa = () => {
console.log(111111)
}
pt(p4).then(res=> {
console.log(res);
}).catch(error=> {
console.log(error);
})
console.log('test')
// test
// 222
十三、 应用
图片加载
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
image.qu
});
}
loadImageAsync('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1587961648939&di=40166f91316c3f23c2964ee73ee72946&imgtype=0&src=http%3A%2F%2Fimg.kutoo8.com%2Fupload%2Fimage%2F95351812%2F001%2520%283%29_960x540.jpg')
.then(img => {
console.log(img);
document.querySelector('body').appendChild(img)
})
最后 说一下前边提到的问题,我现在的是用的常规的方法,就是一个请求完在请求第二个, 当第一个失败了,我就不去请求了,如果有更好的办法,请留言,在这里抱拳了