1、初识Promise
- Promise 是异步操作的一种解决方案
- Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
1.1、Promise的基本用法
- 实例化构造函数生成实例对象
- Promise 的状态 :Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
- Promise 的状态一旦变化,就不会再改变了
- resolve 和 reject 函数的参数
const p = new Promise((resolve, reject) => {
reject(new Error('reason'));
});
p.then(
data => {
console.log('success', data);
},
err => {
console.log('error', err);
}
);
console.log(p);
2、 Promise的构造函数方法
-
Promise.resolve
是成功状态 Promise 的一种简写形式.
//下面两种写法相同
new Promise(resolve => resolve('foo'));
Promise.resolve('foo');
- 参数
一般参数
Promise.resolve('foo').then(data => {
console.log(data);
});
Promise对象:当 Promise.resolve() 接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
// setTimeout(() => {
// resolve('我执行了');
// }, 1000);
});
Promise.resolve(p1).then(data => {
console.log(data);
});
// 等价于
p1.then(data => {
console.log(data);
});
console.log(Promise.resolve(p1) === p1);
当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
new Promise(resolve => resolve(p1)).then(data => {
console.log(data);
});
具有then方法的对象
const thenable = {
then(resolve, reject) {
console.log('then');
resolve('data');
// reject('reason');
}
};
Promise.resolve(thenable).then(
data => console.log(data),
err => console.log(err)
);
console.log(Promise.resolve(thenable));
-
Promise.reject()
失败状态 Promise 的一种简写形式
new Promise((resolve, reject) => {
reject('reason');
});
等价于
Promise.reject('reason');
不管什么参数,都会原封不动地向后传递,作为后续方法的参数
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
});
Promise.reject(p1).catch(err => console.log(err));
new Promise((resolve, rejcet) => {
resolve(123);
})
.then(data => {
// return data;
// return Promise.resolve(data);
return Promise.reject('reason');
})
.then(data => {
console.log(data);
})
.catch(err => console.log(err));
Promise.all()
关注多个 Promise 对象的状态变化,传入多个 Promise 实例,包装成一个新的 Promise 实例返回
Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关
所有状态都变成 resolved,最终的状态才会变成 resolved
只要有一个变成 rejected,最终的状态就变成 rejected
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1 完成了');
// return 'p1';
return Promise.reject('reason');
});
const p2 = delay(2000).then(() => {
console.log('p2 完成了');
return 'p2';
// return Promise.reject('reason');
});
const p = Promise.all([p1, p2]);
p.then(
data => {
console.log(data);
},
err => {
console.log(err);
}
);
-
Promise.race()
Promise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1 完成了');
return 'p1';
});
const p2 = delay(2000).then(() => {
console.log('p2 完成了');
return Promise.reject('reason');
});
const racePromise = Promise.race([p1, p2]);
racePromise.then(
data => {
console.log(data);
},
err => {
console.log(err);
}
);
-
Promise.allSettled()
的状态与传入的Promise 状态无关,永远都是成功的,它只会忠实的记录下各个 Promise 的表现。
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1 完成了');
return 'p1';
});
const p2 = delay(2000).then(() => {
console.log('p2 完成了');
return Promise.reject('reason');
});
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then(data => {
console.log('succ', data);
});
3、Promise的实例方法
-
then
方法- pending->fulfilled 时,执行 then 的第一个回调函数
- pending->rejected 时,执行 then 的第二个回调函数
- then 方法执行后默认是返回一个新的成功状态的Promise对象
const p = new Promise((resolve, reject) => { reject(); }); p.then( () => { // console.log('success'); }, () => { console.log('err'); // 在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下 return 123; // 等价于 // return new Promise(resolve => { // resolve(123); // }); } ) .then( data => { console.log('success2', data); return new Promise(resolve => { resolve(undefined); }); }, err => { console.log('err2', err); } ) .then( data => { console.log('success3', data); }, err => { console.log('err3', err); } );
-
catch
方法,专门用来处理 rejected 状态,本质上是 then 的特例- catch() 可以捕获它前面的错误
- 一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误
new Promise((resolve, reject) => { reject('reason'); }) .then(data => { console.log(data); }) .catch(err => { console.log(err); throw new Error('reason'); }) .then(data => { console.log(data); }) .catch(err => { console.log(err); });
-
finally
,本质上是 then() 的特例。- 当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行
new Promise((resolve, reject) => { // resolve(123); reject('reason'); }) .finally(data => { console.log(data); }) .catch(err => {});
4、Promise的注意事项和应用
- resolve 或 reject 函数执行后的代码
- 推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
- Promise.all/race/allSettled 的参数问题
- 参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象
- Promise.all/race/allSettled 的错误处理
- 错误既可以单独处理,也可以统一处理,一旦被处理,就不会在其他地方再处理一遍
异步加载图片
// 异步加载图片
const loadImgAsync = url => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error(`Could not load image at ${url}`));
};
img.src = url;
});
};
const imgDOM = document.getElementById('img');
loadImgAsync('https://2img.mukewang.com/5f057a6a0001f4f918720764.jpg')
.then(img => {
console.log(img.src);
setTimeout(() => {
imgDOM.src = img.src;
}, 1000);
})
.catch(err => {
console.log(err);
});