前言
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
1.对象里面的状态不受外界影响
2.一旦状态改变就不会在变,任何时候都可以得到这个结果
resolved()统一只指fulfilled状态 ,不包含rejected状态
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
// promise的缺点
1. Promise也有一些缺点。首先,无法取消Promise 一旦新建它就会立即执行,无法中途取消
2.如果不设置回调函数,内部抛出的异常不会反应到外部
3.当处于pending状态时,无法得知目前紧张到什么阶段
.Promise().then(()=>{})
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
const promise = files.map((item, index)=>{
var formData = new FormData(); // formData的格式处理图片数据
formData.append("file", item)
return new Promise((resolve, reject)=>{
handleUpload(formData).then(res=>{
resolve(res)
}).catch(err=>{
reject(err)
})
})
})
const {data} = await Promise.all(promise)
if(data)console.log(data)
.Promise.all()
Promise.all()``方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
只有promise里面的请求全都成功 才会resolve 否则就会reject
promise发请求 是多个请求一起发送 不是发送完一个再发下一个请求
Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
let p1 = new Promise()
let p2 = new Promise()
let p3 = new Promise()
Promise.all([p1, p2]).then((result) => {
console.log(result); // ['成功了', 'success']
}).catch((error) => {
console.log(error) ; // 此时,该步没有走
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result) ; // 此时,该步没有走
}).catch((error) => {
console.log(error); // 失败
})
**Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。**
let wake = (time) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`${time / 1000}秒后醒来`) }, time) })};let p1 = wake(3000);let p2 = wake(2000);Promise.all([p1, p2]).then((result) => { console.log(result); // [ '3秒后醒来', '2秒后醒来' ]}).catch((error) => { console.log(error); // 此时,该步没有走})
.Promise.race()
Promise.race()``方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
Promse.race就是赛跑的意思,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success'); },1000)});let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') ; }, 500)}) ;Promise.race([p1, p2]).then((result) => { console.log(result); // 此时,该步没有走}).catch((error) => { console.log(error); // failed})
.Promise.prototype
Promise.prototype.then()
Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then``方法返回的是一个新的Promise实例(注意,**不是原来那个Promise实例**)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
// 使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,
// 会将返回结果作为参数,传入第二个回调函数。
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
// 上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时, 第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。
简写成es6的形式更简单一点
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
.Promise.any()
概念
参数:接收一个可迭代的对象( iterable),(比如:Array, Map, Set都属于ES6的iterable类型)
返回值:如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
成功(Fulfillment)
当任何一个被传入的 promise 成功的时候,无论其他的 promises 成功还是失败,此函数会将那个成功的 promise 作为返回值 。
如果传入的参数是一个空的可迭代对象,这个方法将会同步返回一个已经完成的promise。
如果传入的任何一个 promise 已成功,或者传入的参数不包括任何 promise, 那么 Promise.any 返回一个异步成功的 promise。
失败/拒绝(Rejection)
如果所有传入的 promises 都失败,Promise.any 将返回异步失败,和一个 AggregateError 对象,它继承自 Error,有一个 errors 属性,属性值是由所有失败值填充的数组。
注意:该方法依然是实验性的,尚未被所有的浏览器完全支持。它当前处于 TC39 第四阶段草案(Stage 4)****
**用法:**
function requset() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求成功')
}, 100)
})
}
function requset1() {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求失败')
}, 300)
})
return p
}
async function getData1() {
const promises = [requset(), requset1()];
const results = await Promise.any(promises);
console.log(results, '2')
}
getData1()
当输入 promises 中的第一个 promise 被执行完成时,results 会立即解析为该 promise 的值; 先完成先返回
function requset() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求成功')
}, 100)
})
}
function requset1() {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求失败')
}, 300)
})
return p
}
async function getData1() {
const promises = [requset(), requset1()];
const results = await Promise.any(promises);
console.log(results, '2')
}
getData1()
Promise.any() 返回的 promise 与任何第一个执行的 promise 一起执行。即使某些 promise 被 rejected,这些 rejections 也将会被忽略。
**如果输入数组中的所有 promises 都被拒绝,或者输入数组为空,那么 Promise.any() 会 rejected 包含输入的 promises 执行的 rejection 错误原因集合。**
Promise.myany = function (args) {
let index = 0;
let errCount = 0; //记录失败的promise执行次数
return new Promise((resolve, reject) => {
// 利用for of 对可迭代对象进行遍历
for (let i of args) {
index++;
Promise.resolve(i).then(
value => {
resolve(value)
},
err => {
errCount++;
if (errCount >= index) {
reject(new AggregateError([], 'All promises were rejected'))
}
}
)
}
})
}
.Promise.resolve()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo')) const p = Promise.resolve();
p.then(function () {
// ...
});
需要注意的是,立即`resolve()`的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
参数是一个thenable对象,thenable对象指的是具有then方法的对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
// Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log(value); // 42
});
.Promise.reject()
`Promise.reject(reason)`方法也会返回一个新的 Promise 实例,该实例的状态为`rejected`。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (res) {
console.log(res)
});
// 出错了
Promise.reject()``方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
Promise.reject('出错了')
.catch(e => {
console.log(e === '出错了') // true
})
.应用 promise来加载图片
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
参考:阮一峰es6, zhuanlan.zhihu.com/p/325870525 , zhuanlan.zhihu.com/p/532452298