01-Promise
# 初识 Promise
# 什么是 Promise
**Promise 是异步操作的一种解决方案。**之前我们就接触过异步的另一种方案:回调函数。
document.addEventListener(
'click',
() => {
console.log('这里是异步的');
},
false
);
console.log('这里是同步的');
2
3
4
5
6
7
8
Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题。
下例中演示了一个回调地狱,这种情况如果需要修改逻辑将非常头疼。
/**
* 移动页面元素的函数
*
* @param el 要转换的元素
* @param {x=0, y=0} 移动的平面距离
* @param end 移动完成后的回调
*/
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.addEventListener(
'transitionend',
() => {
// console.log('end');
end();
},
false
);
};
// 获取要移动的元素
const boxEl = document.getElementById('box');
// 通过不断增加回调,使得元素移动四次(回调地狱)
document.addEventListener(
'click',
() => {
move(boxEl, { x: 150 }, () => {
move(boxEl, { x: 150, y: 150 }, () => {
move(boxEl, { y: 150 }, () => {
// console.log('object');
move(boxEl, { x: 0, y: 0 });
});
});
});
},
false
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Promise 的基本用法
# 实例化 Promise
使用 Promise,先要实例化构造函数生成实例对象,我们用的就是这个实例对象。
// 传入一个回调函数
const p = new Promise(() => {});
2
# Promise 状态
Promise 有 3 种状态:
- 一开始是
pending
(未完成) - 执行 resolve,变成
fulfilled
(resolved),已成功 - 执行 reject,变成
rejected
,已失败
const p = new Promise((resolve, reject) => {
// pending->fulfilled
resolve();
// pending->rejected
// reject();
});
2
3
4
5
6
7
Promise 的状态一旦变化,就不会再改变了。这要如果同时执行 resolve 和 reject,会遵循第一次执行产生的状态。
# then() 方法
Promise 的 then()
方法传入两个回调函数,分别是执行成功和执行失败的回调。
Const p = new Promise((resolve, reject) => resolve());
p.then(
() => console.log('success callback'),
() => console.error('error callback')
);
2
3
4
5
6
# resolve 和 reject 函数的参数
new Promise((resolve, reject) => {
// resolve({ username: 'alex' });
reject(new Error('reason'));
}).then(
data => console.log('success', data),
err => console.log('error', err)
);
2
3
4
5
6
7
resolve 的参数,会传到 then 方法的成功回调函数参数上。
reject 函数的参数,回传到 then 方法的失败回调函数参数上。
# 实例方法
# then()
# then 方法回调的执行
pending
->fulfilled
时,执行 then 的第一个回调函数pending
->rejected
时,执行 then 的第二个回调函数
# then 方法的返回值
then 方法执行后返回一个新的 Promise 对象。所以 then 方法可以链式使用。
new Promise((resolve, reject) => resolve())
.then(() => {}, () => {})
.then();
2
3
那么这个新的 Promise 对象是什么状态呢?
const p1 = new Promise((resolve, reject) => resolve());
const p2 = p1.then(
data => console.log('success1', data),
err => console.log('err1', err)
);
const p3 = p2.then(
data => console.log('success2', data),
err => console.log('err2', err)
);
2
3
4
5
6
7
8
9
10
11
默认 then 方法返回的 Promise 对象都是成功状态(fulfilled
)的。
其实产生的新 Promise 对象就是 then 方法的回调函数返回的。等价于 then 的两个回调都会在最后隐式返回一个成功状态的 Promise 对象。
const p1 = new Promise((resolve, reject) => resolve());
const p2 = p1.then(
data => {
console.log('success1', data);
return new Promise((resolve, reject) => resolve(undefined));
},
err => {
console.log('err1', err);
return new Promise((resolve, reject) => resolve(undefined));
}
);
2
3
4
5
6
7
8
9
10
11
12
即如果 then 回调函数不手动写返回值,会自动返回一个成功状态的 Promise 对象。
如果在 then 回调函数中返回一个非 Promise 对象的数据类型,会将其包装到 Promise 对象回调的 resolve 方法参数处。
const p1 = new Promise((resolve, reject) => resolve());
const p2 = p1.then(
data => {
console.log('success1', data);
return 1;
},
err => {
console.log('err1', err);
return 2;
}
);
// 等同于
const p2 = p1.then(
data => {
console.log('success1', data);
return new Promise((resolve, reject) => resolve(1));
},
err => {
console.log('err1', err);
return new Promise((resolve, reject) => resolve(2));
}
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
如果想要在 then 方法后得到一个失败状态的 Promise 对象怎么做呢?那就只能手动构造一个失败状态的 Promise 对象并返回了。
const p1 = new Promise((resolve, reject) => resolve());
const p2 = p1.then(
data => {
console.log('success1', data);
// 会自动封装成 Promise 对象返回
return 1;
},
err => {
console.log('err1', err);
// 手动返回失败状态的 Promise 对象
return new Promise((resolve, reject) => reject('reason'));
}
);
2
3
4
5
6
7
8
9
10
11
12
13
14
# 回调地狱改造
改造前面写的回调地狱。
/**
* 移动页面元素的函数
*
* @param el 要转换的元素
* @param {x=0, y=0} 移动的平面距离
* @param end 移动完成后的回调
*/
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.addEventListener(
'transitionend',
() => {
// console.log('end');
end();
},
false
);
};
const movePromise = (el, point) => {
return new Promise(resolve => {
move(el, point, () => {
resolve();
});
});
};
// 获取要移动的元素
const boxEl = document.getElementById('box');
document.addEventListener(
'click',
() => {
movePromise(boxEl, { x: 150 })
.then(() => {
return movePromise(boxEl, { x: 0, y: 0 });
})
.then(() => {
return movePromise(boxEl, { x: 150, y: 150 });
})
.then(() => {
return movePromise(boxEl, { y: 150 });
});
},
false
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# catch()
实际使用 then 方法时一般只传入一个参数,即成功状态的回调函数。而失败状态时交给 catch 方法处理。
catch 专门用来处理 rejected 状态,catch 本质上是 then 的特例,相当于 then(null, err => {})
。
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.then(data => console.log(data))
.catch(err => console.log(err));
2
3
4
5
6
catch() 可以捕获它前面的错误,一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误。
# finally()
当 Promise 状态发生变化时,不论如何变化都会执行 finally() 方法,不变化不执行。
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.finally(data => console.log(data))
.catch(err => {});
2
3
4
5
6
finally() 本质上也是 then() 的特例,等同于:
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.then(
result => {
return result;
},
err => {
return new Promise((resolve, reject) => {
reject(err);
});
}
)
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20