这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战
一、Promise 基础
1.1 Promise 含义
Promise 的中文意思是承诺,也就是说,JavaScript 对你许下一个承诺,会在未来某个时刻兑现承诺。
1.2 Promise 状态
一个 Promise 对象值是未知的,状态是可变的,但是无论怎么变化,它的状态永远处于以下三种之间:
- 进行中(pending)
- 已经完成(fulfilled)
- 拒绝(rejected)
Promise 的状态会发生变化,成功时会从 pending -> fulfilled,失败时会从 pending -> rejected,但是此过程是不可逆的,也就是不能从另外两个状态变成 pending。fulfilled/rejected 这两个状态也被称为 settled 状态。
1.3 Promise 意义
Promise的出现是为了解决 ES6 之前 JS 代码中频繁嵌套回调函数所导致的回调地狱问题,Promise 为 ES6 特性。
什么是回调地狱,来看一个最简单的示例:
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
setTimeout(() => {
console.log(444);
// 你还可以放置更多
...
}, 4000);
}, 3000);
}, 2000)
}, 1000);
那么 Promise 怎么解决回调地狱的:
// promise 解决
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
}).then(data => console.log(data));;
}
function f4() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(444), 4000);
}).then(data => console.log(data));;
}
f1().then(f2).then(f3).then(f4);
1.4 Promise 使用
Promise 构造函数只有一个参数,该参数是一个函数,被称作执行器,执行器有 2 个参数,分别是
resolve()和reject(),一个表示成功的回调,一个表示失败的回调。
new Promise(function(resolve, reject) {
setTimeout(() => resolve(5), 0)
}).then(v => console.log(v)) // 5
记住,Promise 实例只能通过 resolve 或者 reject 函数来返回,并且使用 then() 或者 catch() 获取,不能在 new Promise 里面直接 return,这样是获取不到 Promise 返回值的。
1. resolve(value)
Promise.resolve(5).then(v => console.log(v)) // 5
2. reject(value)
Promise.reject(5).catch(v => console.log(v)) // 5
3. 执行器错误通过 catch 捕捉
new Promise(function(resolve, reject) {
if(true) {
throw new Error('error!!')
}
}).catch(v => console.log(v.message)) // error!!
1.5 Promise API
1.5.1 Promise.prototype.then(onFulfilled, onRejected)
可以看到,.then 里面拿到的是我们 Promise resolve 过后的数据。并且他还会返回一个 Promise 继续供我们调用,比如:
new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => {
console.log(data); // 打印 111
return data + 111; // 相当于 resolve(data + 111)
}).then(data => {
console.log(data); // 打印 222
});
.then() 是可以一直链式调用的,因为它的返回值也是一个 Promise。
1.5.2 Promise.prototype.catch(onRejected)
new Promise((resolve, reject) => {
setTimeout(() => reject(111), 1000);
}).then(data => {
console.log('then data:', data);
}).catch(e => {
console.log('catch e: ', e);
});
1.5.3 Promise.prototype.finally(fn)
Promise 提供了标准结束方法 finally(),只要 Promise 状态变成 settled,无论是 rejected 还是 fulfilled,都会在 finally 里捕获。
finally 也会返回一个 promise,但是建议到 finally 就结束了。
具体的使用场景:
let isLoading = true;
fetch(myRequest).then((response) => {
let contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
return response.json();
}
throw new TypeError("Oops, we haven't got JSON!");
})
.then((json) => { console.error(json); })
.catch((error) => { console.error(error); })
.finally(() => { isLoading = false; });
- 参数 fn 是一个无参函数,不论该 promise 最终是 fulfilled 还是 rejected。
- finally 不会改变 promise 的状态。
1.5.4 Promise.all(iterable)
语法很简单:参数只有一个,可迭代对象,可以是数组,或者 Symbol 类型等。
Promise.all(iterable).then().catch()
Promise.all([
new Promise(function(resolve, reject) {
resolve(1)
}),
new Promise(function(resolve, reject) {
resolve(2)
}),
new Promise(function(resolve, reject) {
resolve(3)
})
]).then(arr => {
console.log(arr) // [1, 2, 3]
})
1. 接收一个 Promise 对象数组作为参数:
2. 参数所有回调成功才是成功,返回值数组与参数顺序一致:
可以看到,返回值是一个数组,并且每个元素对应的就是参数数组里对应过后的 resolve 值。
3. 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息
可以看到,当把第二个和第三个分别设置成 reject 的时候,Promise.all 进入了 catch 也就是捕获异常阶段,捕获到的是第二个 reject 内容,也就是第一次出现的 reject 的那个地方。
一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,如果其中一个接口失败了,多个请求也就失败了。