Promise 的理解和使用
Promise 是什么?
理解
-
抽象表达:
- Promise 是一门新的技术(ES6 规范)
- Promise 是 JS 中进行异步编程的新解决方案(旧方案是单纯使用回调函数)
-
具体表达:
- 从语法上来说: Promise 是一个构造函数
- 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
promise 的状态改变
pending
变为resolved
pending
变为rejected
说明: 只有这 2 种,且一个 promise 对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为 value, 失败的结果数据一般称为 reason。
promise 的基本流程
promise 的基本使用
使用 1: 基本编码流程
<script>
// 1) 创建 promise 对象(pending 状态), 指定执行器函数
const p = new Promise((resolve, reject) => {
// 2) 在执行器函数中启动异步任务
setTimeout(() => {
const time = Date.now()
// 3) 根据结果做不同处理
if (time%2===1) {
// 3.1) 如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状态
resolve('成功的值 '+ time)
} else {
// 3.2) 如果失败了, 调用 reject(), 指定失败的 reason, 变为 rejected 状态
reject('失败的值' + time)
}
}, 2000)
}
// 4) 能 promise 指定成功或失败的回调函数来获取成功的 vlaue 或失败的 reason
p.then(value => { // 成功的回调函数 onResolved, 得到成功的 vlaue
console.log('成功的 value: ', value)
}, reason => { // 失败的回调函数 onRejected, 得到失败的 reason
console.log('失败的 reason: ', reason)
});
</script>
使用 2: 使用 promise 封装读取文件的异步
/**
* 封装一个函数 mineReadFile 读取文件内容
* 参数: path 文件路径
* 返回: Promise 对象
*/
function mineReadFile(path) {
return new Promise((resolve, reject) => {
require("fs").readFile(path, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
mineReadFile('../resource/content.txt').then(value => {
console.log(value.toString());
}, reason => {
console.log(reason);
})
使用 3: 使用 promise 封装 ajax 异步请求
<script>
const sendAJAX = function (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
}
sendAJAX("https://api.apiopen.top/getJoke").then(value => {
console.log(value);
}, reason => {
console.log(reason);
});
</script>
为什么要用 Promise?
1、指定回调函数的方式更加灵活
- 以前: 必须在启动异步任务前指定
- promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
2、支持链式调用, 可以解决回调地狱问题
- 回调地狱: 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
- 回调地狱的缺点: 不便于阅读 不便于异常处理
- 解决方案: promise 链式调用
- 终极解决方案: async/await
如何使用 Promise?
Promise 构造函数:
Promise (excutor) {}
executor
函数: 执行器 (resolve, reject) => {}resolve
函数: 内部定义成功时调用的函数:value => {}
reject
函数: 内部定义失败时调用的函数:reason => {}
说明: executor
会在 Promise 内部立即同步调用,异步操作在执行器中执行
Promise API
Promise.prototype.then
Promise.prototype.then(onResolved, onRejected) => {}
onResolved
函数: 成功的回调函数:(value) => {}
onRejected
函数: 失败的回调函数:(reason) => {}
说明: 指定用于得到成功 value
的成功回调和用于得到失败 reason
的失败回调,返回一个新的 promise 对象
Promise.prototype.catch
Promise.prototype.catch(onRejected) => {}
onRejected
函数: 失败的回调函数:(reason) => {}
说明: then()
的语法糖, 相当于: then(undefined, onRejected)
Promise.resolve
Promise.resolve(value) => {}
- value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象
Promise.reject
Promise.reject(reason) => {}
- reason: 失败的原因
说明: 返回一个失败的 promise 对象
Promise.all
Promise.all(promises) => {}
- promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败
Promise.race
Promise.race(promises) => {}
- promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态
promise 的几个关键问题
1、如何改变 promise 的状态?
resolve(value)
: 如果当前是 pending 就会变为 resolvedreject(reason)
: 如果当前是 pending 就会变为 rejected- 抛出异常: 如果当前是 pending 就会变为 rejected
2、一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise 改变为对应状态时都会调用
3、改变 promise 状态和指定回调函数谁先谁后?
- 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
- 如何先改状态再指定回调?
- 在执行器中直接调用
resolve()
/reject()
- 延迟更长时间才调用
then()
- 在执行器中直接调用
- 如何先改状态再指定回调?
- 什么时候才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
4、promise.then() 返回的新 promise 的结果状态由什么决定?
- 简单表达: 由
then()
指定的回调函数执行的结果决定 - 详细表达:
- 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
- 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
5、promise 如何串连多个操作任务?
- promise 的
then()
返回一个新的 promise, 可以开成then()
的链式调用 - 通过 then 的链式调用串连多个同步/异步任务
6、promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
7、中断 promise 链?
- 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
手写 Promise
构造函数版
function Promise(executor) {
this.promiseState = 'pending';
this.promiseResult = null;
this.callbacks = [];
const _this = this;
// resolve 函数
function resolve(data) {
// 判断状态
if (_this.promiseState !== 'pending') return;
// 设置对象状态(promiseState)
_this.promiseState = 'fulfilled';
// 设置对象结果(promiseResult)
_this.promiseResult = data;
// 调用成功的回调函数
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onResolved(data);
});
});
}
// reject 函数
function reject(data) {
// 判断状态
if (_this.promiseState !== 'pending') return;
// 设置对象状态(promiseState)
_this.promiseState = 'rejected';
// 设置对象结果(promiseResult)
_this.promiseResult = data;
// 调用失败的回调函数
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onRejected(data);
});
});
}
try {
// 同步调用 执行器函数
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
// 默认值
if (typeof onResolve !== 'function') {
onRejected = value => value;
}
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason;
}
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
let result = type(_this.promiseResult);
if (result instanceof Promise) {
// 如果是 Promise 类型的对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
});
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 调用回调函数
// 判断 pending 状态
if (this.promiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
} else if (this.promiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
});
} else if (this.promiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved() {
callback(onResolved);
},
onRejected() {
callback(onRejected);
}
});
}
});
}
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
}
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
});
} else {
resolve(value);
}
});
}
Promise.reject = function (value) {
return new Promise((resolve, reject) => {
reject(value);
});
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
const values = [];
const hasReject = false;
for (let i in promises) {
promises[i].then(v => {
values[i] = v;
}, r => {
reject(r);
hasReject = true;
});
}
if (!hasReject) resolve(values);
});
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i in promises) {
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
});
}
});
}
class 版
class Promise {
#promiseState = 'pending';
#promiseResult = null;
#callbacks = [];
// 构造方法
constructor(executor) {
// resolve 函数
const resolve = data => {
// 判断状态
if (this.#promiseState !== 'pending') return;
// 设置对象状态(#promiseState)
this.#promiseState = 'fulfilled';
// 设置对象结果(#promiseResult)
this.#promiseResult = data;
// 调用成功的回调函数
setTimeout(() => {
this.#callbacks.forEach(item => {
item.onResolved(data);
});
});
}
// reject 函数
const reject = data => {
// 判断状态
if (this.#promiseState !== 'pending') return;
// 设置对象状态(#promiseState)
this.#promiseState = 'rejected';
// 设置对象结果(#promiseResult)
this.#promiseResult = data;
// 调用失败的回调函数
setTimeout(() => {
this.#callbacks.forEach(item => {
item.onRejected(data);
});
});
}
try {
// 同步调用 执行器函数
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(
onResolved = value => value, /* ES6默认值 */
onRejected = reason => { throw reason; }
) {
return new Promise((resolve, reject) => {
// 封装函数
const callback = type => {
try {
let result = type(this.#promiseResult);
if (result instanceof Promise) {
// 如果是 Promise 类型的对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
});
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 调用回调函数
// 判断 pending 状态
if (this.#promiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
} else if (this.#promiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
});
} else if (this.#promiseState === 'pending') {
// 保存回调函数
this.#callbacks.push({
onResolved() {
callback(onResolved);
},
onRejected() {
callback(onRejected);
}
});
}
});
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
});
} else {
resolve(value);
}
});
}
static reject(value) {
return new Promise((resolve, reject) => {
reject(value);
});
}
static all(promises) {
return new Promise((resolve, reject) => {
const values = [];
const hasReject = false;
for (let i in promises) {
promises[i].then(v => {
values[i] = v;
}, r => {
reject(r);
hasReject = true;
});
}
if (!hasReject) resolve(values);
});
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i in promises) {
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
});
}
});
}
}
async & await
MDN 文档
async 函数
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
await 表达式
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值
- 如果表达式是其它值, 直接将此值作为 await 的返回值
注意
- await 必须写在 async 函数中, 但 async 函数中可以没有 await
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
评论区