es6从零学习(二):promise
一:promise的由来
某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。由此,Promise的概念就由社区提出并实现,作用与回调方法几乎一致,都是在某种情况下执行预先设定好的方法,但是使用它却能够让代码变得更简洁清晰
二:Promise
对象有以下两个特点。
1、对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
三:promise的缺点
1、无法取消Promise
,一旦新建它就会立即执行,无法中途取消。
2、如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。
3、当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
四:基本用法
下面代码创造了一个Promise
实例。
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
promise.then(function(value) { // success }, function(error) { // failure });
五:promise执行时的特点
1、Promise 新建后就会立即执行。
let promise = new Promise(function(resolve, reject) { console.log(‘Promise‘); resolve(); }); promise.then(function() { console.log(‘resolved.‘); }); console.log(‘Hi!‘); // Promise // Hi! // resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise
。然后,then
方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved
最后输出。
2、resolve
函数的参数除了正常的值以外,还可能是另一个 Promise 实例
const p1 = new Promise(function (resolve, reject) { // ... }); const p2 = new Promise(function (resolve, reject) { // ... resolve(p1); })
上面代码中,p1
和p2
都是 Promise 的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
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
上面代码中,p1
是一个 Promise,3 秒之后变为rejected
。p2
的状态在 1 秒之后改变,resolve
方法返回的是p1
。由于p2
返回的是另一个 Promise,导致p2
自己的状态无效了,由p1
的状态决定p2
的状态。所以,后面的then
语句都变成针对后者(p1
)。又过了 2 秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。
3、调用resolve
或reject
并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
上面代码中,调用resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
new Promise((resolve, reject) => { return resolve(1); // 后面的语句不会执行 console.log(2); })
五:实际应用实例
1、封装一个axios异步调用的函数。
export function myGet(url, params) { return new Promise((resolve, reject) => { axios.get(url, {params}).then(function (response) { resolve(response.data) }) .catch(function (err) { reject(err) }) }) } export function myPost(url, params) { return new Promise((resolve, reject) => { axios.post(url, params).then(function (response) { resolve(response) }) .catch(function (err) { reject(err) }) }) }
调用:
myGet(url, postData).then((res) => { console.log(res) }, (err) => { console.log(err) })