Promise总结
Promise介绍
定义
Promise 是异步编程的一种解决方案:
- 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
- 从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
语法
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise((resolve, reject) => {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
三种状态
- pending(等待态)
- fulfiled(成功态)
- rejected(失败态)
状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
属性
- Promise.length:length属性,其值总是为 1 (构造器参数的数目).
- Promise.prototype:表示 Promise 构造器的原型.
特点
- 对象的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise API概述
1. Promise.prototype.then()
当resolve(成功)/reject(失败)的回调函数
// onFulfilled 是用来接收promise成功的值 // onRejected 是用来接收promise失败的原因 promise.then(onFulfilled, onRejected);
注意:then方法是异步执行的
promise.then方法每次调用都返回一个新的promise对象,所以可以链式写法。
function taskA() { console.log("Task A"); } function taskB() { console.log("Task B"); } function onRejected(error) { console.log("Catch Error: A or B", error); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) // 捕获前面then方法中的异常
2. Promise.prototype.catch()
在链式写法中可以捕获前面then中发送的异常
promise.catch(onRejected) // 相当于 promise.then(null, onRrejected); // 注意:onRejected 不能捕获当前onFulfilled中的异常 promise.then(onFulfilled, onRrejected); // 可以写成: promise.then(onFulfilled) .catch(onRrejected);
3. Promise.prototype.finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
// then返回了一个resolved为2的promise Promise.resolve(1).then(()=>2,()=>3); // Promise {<resolved>: 2} // finally不改变原来resolved的状态 Promise.resolve(1).finally(()=>2); // Promise
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally本质上是then方法的特例。
等价的async function写法
// 1. fulfilled情形 Promise.resolve(1).finally(alert); // Promise {<resolved>: 1} f = async ()=>{ try{ return 1; }finally{ alert(); } }; f(); // Promise {<resolved>: 1} // 2. rejected情形 Promise.reject(1).finally(alert); // Promise {<rejected>: 1} f = async ()=>{ try{ throw 1; }finally{ alert(); } }; f(); // Promise {<rejected>: 1}
4. Promise.resolve()
onFulfilled会被调用,返回一个fulfilled状态的promise对象
const promise = new Promise((resolve, reject) => { resolve('fulfilled'); // 状态由 pending => fulfilled }); promise.then(res => { // onFulfilled 会被调用 console.log(res); // 'fulfilled' }, rej => { // onRejected 不会被调用 }) // promise相当于 Promise.resolve('fulfilled');
5. Promise.reject()
onRejected会被调用,返回一个rejected状态的promise对象
const promise = new Promise((resolve, reject) => { reject('rejected'); // 状态由 pending => rejected }); promise.then(res => { // onFulfilled 不会被调用 }, rej => { // onRejected 会被调用 console.log(rej); // 'rejected' }) // promise相当于 Promise.reject('rejected');
6. Promise.all()
接收一个promise对象数组为参数,只有全部为resolve才会调用。通常会用来处理多个并行异步操作。
谁跑的慢,以谁为准执行回调
const p1 = new Promise((resolve, reject) => { resolve(1); }); const p2 = new Promise((resolve, reject) => { resolve(2); }); const p3 = new Promise((resolve, reject) => { resolve(3); }); Promise.all([p1, p2, p3]).then(data => { // 三个都成功则成功 console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的 }, err => { // 只要有失败,则失败 console.log(err); });
7. Promise.race()
接收一个promise对象数组为参数,Promise.race只要有一个promise对象进入FulFilled或者 Rejected状态的话,就会继续进行后面的处理。
谁跑的快,以谁为准执行回调
function timerPromisefy(delay) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); Promise.race([ timerPromisefy(10), timerPromisefy(20), timerPromisefy(30) ]).then(function (values) { console.log(values); // 10 });
race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
//请求某个图片资源 function requestImg(){ return new Promise((resolve, reject) => { var img = new Image(); img.onload = function(){ resolve(img); } img.src = '图片的路径'; }); } //延时函数,用于给请求计时 function timeout(){ return new Promise((resolve, reject) => { setTimeout(() => { reject('图片请求超时'); }, 5000); }); } Promise.race([ requestImg(), timeout() ]).then(res =>{ console.log(res); }).catch(err => { console.log(err); });
红绿灯问题
题目:红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用 Promise 实现)
三个亮灯函数已经存在:
function red(){ console.log('red'); } function green(){ console.log('green'); } function yellow(){ console.log('yellow'); }
利用 then 和递归实现:
var light = function(timmer, cb){ return new Promise(function(resolve, reject) { setTimeout(function() { cb(); resolve(); }, timmer); }); }; var step = function() { Promise.resolve().then(function(){ return light(3000, red); }).then(function(){ return light(2000, green); }).then(function(){ return light(1000, yellow); }).then(function(){ step(); }); } step();
Promise局限性
1. 吞掉错误或异常(顺序错误处理)
throw new Error('error'); console.log(1234567);
在这种情况下,因为 throw error 的缘故,代码被阻断执行,并不会打印 1234567。
const promise = new Promise(null); console.log(1234567);
以上代码依然会被阻断执行,这是因为如果通过无效的方式使用 Promise,并且出现了一个错误阻碍了正常 Promise 的构造,结果会得到一个立刻跑出的异常,而不是一个被拒绝的 Promise。
let promise = new Promise(() => { throw new Error('error') }); console.log(1235467);
这次会正常的打印 1234567,说明 Promise 内部的错误不会影响到 Promise 外部的代码,而这种情况我们就通常称为 “吞掉错误”。
其实这并不是 Promise 独有的局限性,try..catch 也是这样,同样会捕获一个异常并简单的吃掉错误。
而正是因为错误被吃掉,Promise 链中的错误很容易被忽略掉,这也是为什么会一般推荐在 Promise 链的最后添加一个 catch 函数,因为对于一个没有错误处理函数的 Promise 链,任何错误都会在链中被传播下去,直到你注册了错误处理函数。
2. 单一值
Promise 只能有一个完成值或一个拒绝原因,然而在真实使用的时候,往往需要传递多个值,一般做法都是构造一个对象或数组,然后再传递,then 中获得这个值后,又会进行取值赋值的操作,每次封装和解封都无疑让代码变得笨重。
建议是使用 ES6 的解构赋值:
Promise.all([Promise.resolve(1), Promise.resolve(2)]) .then(([x, y]) => { console.log(x, y); });
3. 无法取消
Promise 一旦新建它就会立即执行,无法中途取消。
4. 无法得知 pending 状态
当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。