文章目录
1. 认识Promise
- Promise 是一门新技术(ES6规范)
- Promise 是 JS 中进行异步编程新的解决方案,旧的使用的是回调函数
- 从语法上来说,Promise 是一个构造函数
- 从功能上来说,Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果、
- Promise.then 之后会返回一个新的 Promise 对象,支持链式调用,可以解决回调地狱的问题
2. Promise初体验
简单案例
// 实现一个彩票中奖的案例,中奖概率为30%,公共代码
// 生成一个 min~max 之间的随机数
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
// 获取点击按钮
let btn = document.querySelector("button")
// 不基于Promise实现
btn.addEventListener("click", function () {
setTimeout(() => {
if (random(1, 100) <= 30) {
alert("恭喜你中奖了")
} else {
alert("很遗憾没中奖")
}
}, 2000)
})
// 基于Promise实现
btn.addEventListener("click", function () {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
let n = random(1, 100)
if (n <= 30) {
resolve(n)
} else {
reject(n)
}
}, 2000)
})
p.then(res => {
alert("恭喜你中奖了" + res)
}).catch(err => {
alert("很遗憾没中奖" + err)
})
})
3. Promise的状态与值
3.1 状态
-
实例对象中的一个属性 [PromiseState],一个 Promise 对象只能改变一次成功或失败的状态
-
pending:待定
-
resolved/fullfilled:成功
-
rejectd:失败,与成功只能有一个
-
settled/resolved:以敲定/已决议,表示 Promise 对象无论成功还是失败,都会进入这个状态
3.2 值
- 实例对象中的一个属性 [PromiseResult]
- 保存着对象成功/失败的结果
- 只有 resolve 和 reject 才能改变这个值
4. Promise方法的使用
4.1 then、finally
-
then
// 只使用 then 返回异常 function sum(a, b) { return new Promise((resolve, reject) => { if (a && b) { resolve(a + b) } else { reject("出错了") } }) } // then 可以传两个参数(成功,失败) sum(5).then(res => { console.log(res); }, err => { console.log(err); }) // 出错了
注意:
Promise 可以连续 .then,最后跟一个 .catch 就可以了(异常穿透)
.then 返回的一个新的 Promise 对象状态为成功,新 Promise 对象的值为 return 的值
-
finally
// finally 无论失败与成功都会执行它 function sum(a, b) { return new Promise((resolve, reject) => { if (a && b) { resolve(a + b) } else { reject("出错了") } }) } // 成功 sum(5, 10).then(res => { console.log(res); }).catch(err => { console.log(err); }).finally(() => { console.log("我会被执行"); }) // 15 // 我会被执行 // 失败 sum(5).then(res => { console.log(res); }).catch(err => { console.log(err); }).finally(() => { console.log("我会被执行"); }) // 出错了 // 我会被执行
4.2 Promise静态方法
- Promise.all(
iterable
):这个方法返回一个新的 promise 对象,返回多个 promise 成功的值,并存放数组中,如果有一个失败 all 就会返回那个失败 - Promise.any(
iterable
):接收一个 Promise 对象的集合,当其中的一个 promise 成功,就返回那个成功的 promise 的值,如果全部失败,会抛出全部承诺被拒绝 - Promise.allSettled(
iterable
):返回一个 promise 对象,无论成功失败,都会把对应的状态和值返回 - Promise.race(
iterable
):只会返回其中一个处理好的 promise,无论成功还是失败 - Promise.reject(
err
):相当于直接调用 .catch,如果参数是一个 promise 对象,会返回这个 Promise 对象 - Promise.resolve(
res
):相当于直接调用 .then,如果参数是一个 promise 对象,那么这个 promise 对象返回的值,就是 resolve 的值
5. Promise的实现
class Promise {
constructor(executor) {
// 待定 pending
// 成功 fullfilled
// 失败 rejectd
this.PromiseState = "pending"
this.PromiseResult = undefined
// 保存 then 中的回调,可能会被调用多次
this.callbacks = []
let self = this
// 抛出异常也为失败回调
// Promise 中只为同步抛出异常做了处理,异步没有
try {
// Promise 中的回调函数
executor(resolve, reject)
} catch (e) {
reject(e)
}
// 成功
function resolve(res) {
// 只能改变一次状态的值
if (self.PromiseState === "pending") {
// 1.改变状态
self.PromiseState = "fullfilled"
// 2.改变返回值
self.PromiseResult = res
// 如果 resolve 为同步,callbacks 为空不会被调用 then 中的方法
if (self.callbacks.length !== 0) {
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolve(res)
})
});
}
}
}
// 失败
function reject(err) {
if (self.PromiseState === "pending") {
self.PromiseState = "rejectd"
self.PromiseResult = err
if (self.callbacks.length !== 0) {
setTimeout(() => {
self.callbacks.forEach(item => {
item.onReject(err)
})
});
}
}
}
}
// .then
then(onResolve, onReject) {
// 处理异常穿透
if (typeof onReject !== "function") {
// 如果 then 中没有处理失败,那么就一值向传
onReject = err => {
throw err
}
}
// 值传递
if (typeof onResolve !== "function") {
// 如果 then 中没有写成功的回调,那么就给它创建一个
onResolve = res => res
}
return new Promise((resolve, reject) => {
// 模拟异步任务
setTimeout(() => {
// 两个回调只能执行一个
if (this.PromiseState === "fullfilled") {
this.thenAll(onResolve, resolve, reject)
}
if (this.PromiseState === "rejectd") {
this.thenAll(onReject, resolve, reject)
}
// 如果 resolve() 或 reject() 为异步
// 就把函数存入数组中,让 resolve 来调用
if (this.PromiseState === "pending") {
this.callbacks.push({
// 延迟成功的处理
onResolve: () => {
this.thenAll(onResolve, resolve, reject)
},
// 延迟失败的处理
onReject: () => {
this.thenAll(onReject, resolve, reject)
}
})
}
})
})
}
// then 中公共的逻辑
thenAll(on, resolve, reject) {
try {
let result = on(this.PromiseResult)
if (result instanceof Promise) {
// 借助 then 中返回的的值,来确定返回 Promise 的值与状态
// 如果没有返回或返回其他值,默认为成功的状态
result.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
// .catch
catch(onReject) {
return this.then(undefined, onReject)
}
// Promise.resolve
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(value)
}
})
}
// Promise.reject
static reject(value) {
return new Promise((resolve, reject) => {
reject(value)
})
}
// Promise.all
static all(promises) {
if (!Array.isArray(promises)) throw "必须为可迭代对象"
return new Promise((resolve, reject) => {
let arr = []
let count = 0
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
// 不确定哪个先执行,要保证顺序不被打乱
arr[i] = res
count++
if (count === promises.length) {
resolve(arr)
}
}, err => {
// 有一个失败就为失败
reject(err)
})
}
})
}
// Promise.race
static race(promises) {
if (!Array.isArray(promises)) throw "必须为可迭代对象"
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
// 因为状态只能修改一次,所以不需要加任何判断
promises[i].then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
}
6. async、await
6.1 async
async 函数会返回一个 Promise 对象,和 Promise.resolve 有点相似
// 正常同步执行
function fn(a, b) {
return a + b
}
console.log(111);
console.log(fn(10000, 10000));
console.log(222);
// 111
// 20000
// 222
// 加了 async,并没有改变代码执行顺序
// async 函数会返回一个 Promise 对象,Promise 是同步的
async function fn(a, b) {
return a + b
}
console.log(111);
console.log(fn(10000, 10000));
console.log(222);
// 111
// Promise {<fulfilled>: 20000}
// 222
// Promise 有.then 和 .catch 方法,是异步的
// 因为 async 函数会返回一个 Promise 对象,所以可以使用 .then
async function fn(a, b) {
return a + b
}
console.log(111);
fn(10000, 10000).then(res => {
console.log(res);
})
console.log(222);
// 111
// 222
// 20000 ==> 异步的丢掉最后执行的
6.2 await
- await 操作符用于等待一个 Promise 对象。它只能在异步函数 async function 或 模板顶层 type=“module” 使用
- await 表达式会暂停当前 async function 的执行,并不会阻塞外部同步代码的执行,等待 async function 函外部的同步代码执行完成以后,才会在执行 await 之后的代码
- await 返回 Promise 对象的成功结果,如果为失败会把异常原因抛出(如果没有使用 await 抛出,外部无法使用 try{}catch{}去捕获,只能使用 .catch 内部捕获)。如果等待的不是 Promise 对象,则返回该值本身
- await 等待之后的代码会放入微任务队列,但是紧跟在 await 后面的代码为同步的(下面案例的 333)
async function fn(){
console.log(111);
let count = await 333
console.log(count);
}
fn()
console.log(222);
// 111
// 222
// 333 await 微任务
new Promise((resolve, reject) => {
resolve(444)
}).then(res => {
console.log(res);
})
async function fn() {
console.log(111);
let count = await 333
console.log(count);
}
fn()
console.log(222);
// 111
// 222
// 444 .then 微任务,先进入的微任务队列
// 333 await 微任务,后进入的微任务队列
setTimeout(() => {
console.log(444);
})
async function fn() {
console.log(111);
let count = await 333
console.log(count);
}
fn()
console.log(222);
// 111 同步
// 222 同步
// 333 await 微任务
// 444 setTimeout 宏任务最后执行
async function fn() {
// 接收 Promise 成功的值
let result = await new Promise((resolve, reject) => {
resolve(111)
})
console.log(result);
}
fn() // 111
async function fn() {
// 抛出 Promise 失败的值
let result = await new Promise((resolve, reject) => {
reject(111)
})
console.log(result);
}
// 并没有使用 catch 去捕获
fn() // Uncaught (in promise) 111
7. 特殊用法 thenable
当一个函数或对象有 then 方法时,可以作为 Promise 使用
let obj = {
then(resolve, reject) {
resolve("ok1");
},
};
function fn() {}
fn.then = function (resolve, reject) {
resolve("ok2");
};
// 在 Promise 中会默认调用 then 方法,此刻的 then 和 new Promise 很相似
Promise.resolve(obj)
.then((res) => {
console.log(res); // "ok1"
return fn;
})
.then((res) => {
console.log(res); // "ok2"
});
8. 关于 thenable 会慢一步,Promise.resolve 会慢两步的问题
8.1 thenable 慢一步
// 使用 thenlable 会多生成一个回调会导致慢一步
let obj = {
then(resolve, reject) {
resolve();
},
};
function fn() {}
fn.then = function (resolve, reject) {
resolve();
};
Promise.resolve(obj)
.then((res) => {
console.log("ok1");
return fn;
})
.then((res) => {
console.log("ok2");
});
Promise.resolve().then(()=>{
console.log("ok3")
})
// ok3 ok1 ok2
8.2 Promise.resolve 慢两步
// 如果在 then 中返回一个 Promise.resolve,会把 then 中整个 then 函数丢到微任务队列
// 这样在微任务队列中会再次调用 then 生成回调,导致慢两步
Promise.resolve()
.then(() => {
console.log(0);
return Promise.resolve();
})
.then(() => {
console.log(4);
});
Promise.resolve()
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
});
// 0 1 2 3 4 5 6
8.3 两种情况结合
let obj = {
then(resolve, reject) {
resolve();
},
};
function fn() {}
fn.then = function (resolve, reject) {
resolve();
};
Promise.resolve(obj)
.then(() => {
console.log("ok1");
})
.then(() => {
console.log("ok2");
})
.then(() => {
console.log("ok5");
});
Promise.resolve()
.then(() => {
console.log("ok3");
return Promise.resolve();
})
.then((res) => {
console.log("ok4");
});
// ok3 ok1 ok2 ok5 ok4