Promise的概念
Promise A+
规范
- 根据
Promise A+
规范所言,Promise
是一个带有then
方法的对象或函数 - 只要满足
Promise A+
规范,其就是Promise
javascript
// 对象
const Promise = {
then(...){}
}
// 函数
function Promise(){}
Promise.then = function(...){}
- 只要是一个
Promise
,就可以与另一个Promise
进行互操作
ES6
中的Promise
构造函数
- 在
ES6
中的Promise
构造函数,称之为对Promise A+
规范的实现 - 通过该构造函数可以构建一个
promise
实例
javascript
new Promise()
- 但是严格来说该构造函数本身并不是一个
Promise
,因为它没有then
方法,但是通过该构造函数创建的实例带有满足Promise A+
的then
方法
javascript
const p = new Promise()
p.then(...)
为何需要Promise
- 一项新技术的诞生,目的是为了解决旧技术的存在的痛点和缺陷
- 在没有
Promise
的时代,一些异步操作通常需要通过回调函数去获取异步操作的结果 - 当异步操作变得繁琐,或者获取异步操作之后还需要进行另一个异步操作,那么回调函数就会层层嵌套,形成回调地狱
回调地狱
- 回调地狱是形容在异步编程中出现的多层嵌套回调函数的现象
- 当异步操作依赖于前一个操作的结果时,为了保证顺序和正确性,常常会使用回调函数来处理这些操作,如果嵌套的函数层级过多,代码会变得冗余,耦合性强,并且难以维护
javascript
request('/login', (res) => {
console.log('登录成功');
request('/user/info', (res) => {
console.log('获取用户信息');
request('/home/list', (res) => {
console.log('获取首页数据');
request('xxx', (res) => {
...
})
});
});
});
Promise
的出现解决的痛点
- 解决回调地狱: 通过
then
方法的链式操作,让回调嵌套的变成链式调用,使得代码更加直观 - 个人理解:
Promise
并没有真正意义上解决回调地狱的嵌套问题,而是让回调方式变得更加直观
javascript
const promise = new Promise()
promise.then(...)
.then(...)
.then(...)
- 统一 JavaScript 中的异步方案: 提供简洁的链式调用和简易的捕获错误机制,而且支持异步并发执行
javascript
const promise = new Promise()
promise.then(...).catch(...)
Promise的基本使用
Promise
构造函数的使用语法如下
javascript
const promise = new Promise((resolve, reject) => {
// 异步任务操作...
});
Promise
接受一个函数作为参数,该函数被称为executor
,在new Promise
时,executor
会立即自动执行,executor
函数还提供两个函数作为参数
executor
函数的两个参数
resolve(value)
: 当任务成功完成时调用,并附带成功结果value
reject(error)
: 当任务出现错误时调用,并抛出错误结果error
获取
promise
实例返回的结果
- 当调用
resolve
回调函数时,会执行Promise
实例的then
方法传入的回调函数 - 当调用
reject
回调函数时,会执行Promise
实例的catch
方法传入的回调函数
javascript
const promise1 = new Promise((resolve, reject) => {
reslove('success')
});
promise1.then((res) => {
console.log(res) // 'success'
})
const promise2 = new Promise((resolve, reject) => {
reject('error')
});
promise2.catch((err) => {
console.log(err) // 'error'
})
- 也可以通过
then
方法传入的第二个回调函数捕获错误,相当于使用catch
捕获错误
javascript
const promise = new Promise((resolve, reject) => {
reject('error');
});
promise3.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err); // 'error'
}
);
// 以上写法和下面是一致的
promise3.then((res) => {
console.log(res);
})
promise3.catch((err) => {
console.log(err); // 'error'
})
总结:
new Promise()
后,executor
函数自动执行- 执行成功则调用
resolve(value)
,通过then
方法传入的第一个函数获取成功结果 - 执行出错后则调用
reject(err)
,通过then
方法传入的第二个函数(或catch
方法)捕获错误结果
Promise的三种状态
Promise
的使用过程中划分为三个状态 :pending(待定)
、fulfilled(已兑现)
、rejected(已拒绝)
javascript
const promise = new Promise((resolve, reject) => {
// pending
if(success) {
resolve('success');
}else {
reject('error')
}
})
promise.then((res) => {
// fulfilled
console.log(res);
})
promise.catch((err) => {
// rejected
console.log(err);
})
- 待定(pending): 初始状态,既没有被兑现也没有被拒绝,当执行
executor
中的代码时处于该状态 - 已兑现(fulfilled): 意味着操作成功完成,调用
resolve
后处于该状态 - 已拒绝(rejected): 意味着操作失败,调用
reject
后或抛出异常处于该状态
注意:
Promise
状态一旦确定就是不可更改的(锁定)
resolve参数
resolve
函数的参数可以有多种格式
- 普通值或对象,参数可在
then
方法的回调中获得,状态由pending
-->fulfilled
javascript
new Promise((resolve, reject) => {
resolve({ name:'Jimmy' });
}).then((res) => {
console.log(res); // '{ name:'Jimmy' }',此时状态为fulfilled
})
- 传入一个
Promise
,那么当前Promise
的状态将由传入的Promise
来决定,相当于状态进行移交
javascript
const newPormise = new Promise((resolve, reject) => {
reject('error'); // 拒绝
});
new Promise((resolve, reject) => {
resolve(newPormise); // 传入一个Promise,当前Promise状态取决于newPromise
}).then(
(res) => {
console.log('res:', res);
},
(err) => {
console.log('err:', err);
}
);
- 传入带
then
方法的对象,那么该对象then
方法会执行,且同时决定Promise
状态
javascript
const obj = {
then(resolve, reject) {
reject('error');
}
};
new Promise((resolve, reject) => {
resolve(obj);
}).then(
(res) => {
console.log('res:', res);
},
(err) => {
console.log('err:', err); // err: error
}
);
Promise对象方法
then()
then
方法接收两个回调函数作为参数,分别在状态更改为fulfilled
或rejected
时调用
then
方法接收的两个参数
fulfilled
的回调函数: 当状态变成fulfilled
时会执行的函数rejected
的回调函数: 当状态变成rejected
时会执行的函数
javascript
const promise = new Promise((resolve, reject) => {
resolve('success');
});
promise.then((res) => {
console.log('res:', res); // 'res:success'
},(err) => {
console.log('err:', err);
});
同一个
promise
对象可以被调用多次then
方法
- 当调用
resolve
方法时,所有的then
方法传入的回调函数都会被调用
javascript
promise.then((res) => {
console.log('res:', res); // res: success
});
promise.then((res) => {
console.log('res2:', res); // res2: success
});
promise.then((res) => {
console.log('res3:', res); // res3: success
});
then
方法传入的回调可以有返回值,并且then
方法本身的返回值是一个Promise
- 传入的回调返回普通值,该值会作为
then
方法本身返回的Promise
的resolve
值
javascript
promise
.then((res) => {
return { name: 'Jimmy' };
})
.then((newRes) => {
console.log(newRes); // { name: 'Jimmy' }
});
- 传入的回调返回
Promise
,那么then
方法本身返回的Promise
的状态取决于回调中返回的Promise
javascript
promise
.then((res) => {
return new Promise((resolve, reject) => {
reject(123); // 'rejected'
});
})
.then(
(newRes) => {
console.log(newRes);
},
(err) => { // 触发第二个回调
console.log(err); // 123
}
);
- 传入的回调返回带
then
方法的对象,那么then
方法本身返回的Promise
的状态取决于返回对象中的then
方法
javascript
promise
.then((res) => {
return {
then(resolve, reject) {
resolve(456);
}
};
})
.then((newRes) => {
console.log(newRes); // 456
});
catch()
- 当
executor
函数调用reject
,或者抛出异常时,都会触发catch
方法捕获错误
javascript
const promise = new Promise((resolve, reject) => {
reject('error');
});
promise.catch((err) => {
console.log(err); // error
});
- 无论是在
Promise
的executor
函数,还是其对象的then
方法中,只要有reject()
或异常抛出,catch
方法都会捕获
javascript
const promise = new Promise((resolve, reject) => {
resolve('success');
});
promise
.then((res) => {
console.log(res); // 'success'
throw new Error('error');
})
.catch((err) => {
// 捕获then方法中返回的promise抛出的错误
console.log(err); // Error对象
});
javascript
const promise = new Promise((resolve, reject) => {
reject('error');
});
promise
.then((res) => {
// reject调用,这里是不会执行的
return new Promise((resolve, reject) => {
resolve('success');
});
})
.catch((err) => {
// 这里捕获的是promise的异常,而不是then方法中返回的Promise的异常
console.log(err); // error
});
catch
方法也是可以有返回值的,并且本身也是返回一个Promise
- 当在
catch
方法中的回调返回内容后,返回的内容和catch
本身返回的Promise
的状态是有关联的
javascript
const promise = new Promise((resolve, reject) => {
reject('error');
});
promise
.catch((err) => {
return 'catch return value'; // 相当于在返回的Promise中resolve('catch return value')
})
.then((res) => {
console.log('then:', res); // then: catch return value
})
.catch((err) => {
console.log('catch:', err);
});
finally()
finally
是在ES2018
中新增特性,它表示无论Promise
对象变成fulfilled
还是rejected
状态,该方法内的代码最终都会被执行finally
方法不接收参数,因为无论前面是fulfilled
状态,还是rejected
状态,它都会执行
javascript
const promise = new Promise((resolve, reject) => {
resolve('resolve message');
});
promise
.then((res) => {
console.log('res:', res); // res: resolve message
})
.catch((err) => {})
.finally(() => {
console.log('finally code execute'); // finally code execute
});
finally
的功能是设置一个处理程序在前面的操作完成后,执行清理,如关闭loading
,终止某些连接等
javascript
showLoading(); // 显示loading遮罩
promise
.then((data) => {
console.log('服务器返回的数据:', data);
})
.finally(() => {
hideLoading(); // 请求结束,处理对应数据后,关闭loading遮罩
});
- 由于
finally
不处理promise
的结果,所以可以将结果或错误传递给下一个合适的处理程序
javascript
new Promise((resolve, reject) => {
resolve('value');
})
.finally(() => console.log('Promise ready')) // 先触发
.then((res) => console.log(res)); // "value"
finally
方法的回调函数不返回任何内容,就算有返回值也会默认被忽略
javascript
const promise = new Promise((resolve, reject) => {
resolve('value');
})
promise
.finally(() => 'finally return value';) // 返回值会被忽略
.then((res) => console.log(res)); // 这里输出的是 "value"
- 但是
finally
的回调函数可以抛出error
, 执行将转到最近的error
的处理程序
javascript
const promise = new Promise((resolve, reject) => {
resolve('value');
})
promise
.finally(() => { throw new Error('error'); }) // 先触发
.catch((err) => console.log(err)); // 这里输出finally抛出的错误对象
Promise类方法
then
、catch
、finally
方法都属于Promise
的实例方法,存在于Promise.prototype
上- 在
Promise
类上也有一些静态方法可以使用, 一共有 6 种静态方法
resolve()
- 用结果
value
创建一个fulfilled
状态的promise
,其用法相当于new Promise
并执行resolve
javascript
Promise.resolve('success');
// 相当于
new Promise(resolve => resolve('success'))
- 当已经确定返回结果,并且期望返回一个
Promise
时,可以使用该方法
Promise.resolve
的参数形态 :
- 普通的值或者对象
javascript
Promise.resolve('success');
- 本身是
Promise
,并且resolve
方法本身返回的Promise
状态会取决于参数中的Promise
javascript
const promise2 = Promise.resolve(
new Promise((resolve, reject) => {
reject('error');
})
);
promise2.catch((err) => {
console.log(err); // error
});
- 带
then
方法的对象,该对象的then
方法也决定resolve
本身返回的Promise
的状态
javascript
const promise3 = Promise.resolve({
then(resolve, reject) {
reject('error');
}
});
promise3.catch((err) => {
console.log(err); // error
});
reject()
- 用
error
创建一个rejected
状态的Promise
javascript
Promise.reject('error message')
// 等同于
new Promise((resolve, reject) => reject('error message'))
reject
不分情况,不管传入的参数是什么,最后都会返回一个rejected
状态的Promise
- 在开发中,这个方法几乎从未被使用过
all()
- 有时候希望并行执行多个
promise
,并等待所有promise
都准备就绪后,再对数据进行处理 - 这时候可以使用
Promise.all()
,它接受一个可迭代对象(通常是一个promise
数组),并返回一个包含结果的数组 - 当所有给定的
promise
都fulfilled
状态时,Promise.all()
才完成
javascript
const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 500));
Promise.all([promise1, promise2, promise3]).then((res) => {
console.log(res); // 3 秒之后输出 [ 1, 2, 3 ]
});
- 注意: 结果数组中元素的顺序与传入的
promise
顺序相同,即使第一个promise
等待时间最长才fulfilled
,但仍是结果数组中的第一个 - 如上所示,第一个放入的是等待
1500ms
才完成的promise
,在返回结果中它仍是第一个 - 当传入的数组中包含不是
Promise
的项,其内部会使用Promise.resolve
进行转换
javascript
Promise.all([promise1, promise2, promise3, 'abc']).then((res) => {
console.log(res); // [ 1, 2, 3, 'abc' ]
});
- 如果任意一个
promise
变成rejected
,那么Promise.all
返回的 promise 的状态也是rejected
,并且将错误信息 error 返回
javascript
const promise1 = new Promise((resolve, reject) => setTimeout(() => reject('error'), 1500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 500));
Promise.all([promise1, promise2, promise3, 'abc']).catch((err) => {
console.log(err); // error
});
- 注意: 如果其中一个
promise
状态变成rejected
,错误信息回立马返回,Promise.all
马上变成rejected
,其他promise
的结果均会被忽略,但操作仍需执行
javascript
const promise1 = new Promise((resolve, reject) => setTimeout(() => reject('error message'), 500));
const promise2 = new Promise((resolve) =>
setTimeout(() => {
console.log('会执行');// 这里会输出
resolve(2);
}, 3000)
);
const promise3 = new Promise((resolve) =>
setTimeout(() => {
console.log('会执行'); // 这里会输出
resolve(3);
}, 3000)
);
Promise.all([promise1, promise2, promise3, 'abc']).catch((err) => {
console.log(err); // error message
});
allSettled()
all
方法的缺陷: 当其中一个Promise
变成rejected
状态时,Promise.all
返回的新Promise
会立即变成rejected
状态,对于fulfilled
或仍处于pending
状态的Promise
,是无法获取对应结果的,而且也没有采取任何措施来取消- 在
ES2020
中添加了新的API
--Promise.allSettled
,该方法会在所有传入的Promise
状态都变成fulfilled
或rejected
后,才会有最终的状态,并且返回的Promise
一定是fulfilled
的
javascript
const promise1 = new Promise((_, reject) => setTimeout(() => reject('error'), 500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1500));
Promise.allSettled([promise1, promise2, promise3]).then((res) => {
console.log(res);
});
- 该方法仍然返回一个数组,但是该数组由对象组成,如
{ status: "fulfilled", value: result }
- 该方法在一些旧的浏览器中可能需要
polyfills
,可以使用all
方法实现allSettled
javascript
const rejectHandler = (reason) => ({ status: 'rejected', reason });
const resolveHandler = (value) => ({ status: 'fulfilled', value });
Promise.myAllSettled = (promises) => {
const newPromises = promises.map((p) => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(newPromises);
};
race()
- 与
Promise.all
类似,但只等待第一个完成的promise
并获取其结果或错误信息
javascript
const promise1 = new Promise((_, reject) => setTimeout(() => reject('error message'), 1500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 500));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1500));
Promise.race([promise1, promise2, promise3]).then(console.log); // 2
- 当其中一个
Promise
状态变成fulfilled
或rejected
,Promise.race
返回的新Promise
也完成,其状态取决于最快完成的Promise
- 当一个
Promise
完成后,后面所有的Promise
都会被忽略,但是操作仍继续执行
any()
any
方法是ES2021
中新增的方法,与Promise.race
类似
与
race
方法的区别
Promise.any
只等待第一个fulfilled
的promise
,并将其返回,然后决定新返回Promise
的状态
javascript
const promise1 = new Promise((_, reject) => setTimeout(() => reject('error message'), 500));
const promise2 = new Promise((_, reject) => setTimeout(() => reject('error message2'), 1000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 1500));
// 这里会等待1.5秒后输出3,reject的promise是忽略的
Promise.any([promise1, promise2, promise3]).then(console.log); // 3
- 如果所有
promise
都是rejected
状态,那么any
方法会返回带有AggregateError
对象的promise
,其errors
属性中存储着所有错误信息
javascript
const promise1 = new Promise((_, reject) => setTimeout(() => reject('error message'), 500));
const promise2 = new Promise((_, reject) => setTimeout(() => reject('error message2'), 1000));
const promise3 = new Promise((_, reject) => setTimeout(() => reject(3), 1500));
Promise.any([promise1, promise2, promise3]).catch(console.log);