一、基本使用
Promise是一个构造函数, 该对象用来封装一个 异步操作 并可以获取其成功/失败的结果值.
Promise的状态(PromiseState): pending 等待 | resolved、fulfilled 成功 | rejected 失败
一个Promise对象只能改变
一次状态
:pending -> resolved
||pending -> rejected.
Promise的值(PromiseResult): 保存着异步任务【成功、失败】的结果.无论是状态还是实例对象的值 改变都是 调用resolve
或reject
方法所导致的.Promise的执行器(实例对象里面参数箭头函数)是同步立即执行的!
Promise实例对象
原型上
有then
、catch
、finally
方法
const p = new Promise((resolve, reject) => {
let num = Math.floor(Math.random() * 100 + 1);
if (num <= 30) {
resolve(num);
} else {
reject(num)
}
})
// p.then((num) => {
// alert(`恭喜您中奖了,中奖数字为${num}`)
// }, (num) => {
// alert(`很遗憾再接再厉,数字为${num}`)
// })
p.then(value => alert(`恭喜您中奖了,中奖数字为${value}`)).catch(reason => alert(`很遗憾再接再厉,数字为${reason}`))
二、静态方法
2.1 resolve
- resolve参数为非Promise类型的对象,则返回结果为成功的promise对象
let p1 = Promise.resolve(521);
console.log(p1);
- 参数为Promise类型的对象,则返回结果由执行器内 看是成功调用还是失败调用导致结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// 执行器会同步立即执行
// resolve('ok')
reject('error');
}))
p2.catch(err => console.log(err))
console.log(p2); // 如果是错误的返回结果我们不处理浏览器会报错Uncaught (in promise) error
2.2 reject
无论参数是啥其状态都是
rejected
let p3 = Promise.reject(521);
let p4 = Promise.reject(new Promise((resolve, reject) => {
throw reject('失败');
}));
p3.catch(err => console.log(err))
p4.catch(err => console.log(err))
console.log(p3);
console.log(p4);
2.3 all
只要数组中有一个失败,结果返回失败的Promise
let p1 = new Promise((resolve, reject) => {
resolve('ok')
});
// let p2 = Promise.resolve('xxxx');
let p2 = Promise.reject('error');
const result = Promise.all([p1, p2]);
console.log(result);
console.log(result.then(res => console.log(res)));
console.log(result.catch(err => console.log(err)));
2.4 race
只要数组中哪个先成功 结果返回那个,一旦有失败的结果返回失败
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000)
});
let p2 = Promise.resolve('xxxx');
// let p2 = Promise.reject('error');
let p3 = Promise.resolve('yyyy');
const result = Promise.race([p1, p2, p3]);
console.log(result);
三、链式调用
let p = new Promise((resolve, reject) => {
resolve('ok');
})
let result = p.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve('success');
})
}).then(value => {
console.log(value);
return new Promise((resolve, reject) => {
reject('error');
})
})
.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
.then(value => {
console.log(value); // undefined
return 521
})
console.log(result);
结论:
- result 的 结果由
最后一个
then决定 - 每个打印的结果 要去上一级的then方法中 看返回的Promise 调用的是谁 决定了 下一个then走哪个回调函数
- 当上一级没有返回Promise , 结果为undefined 我们可知then方法的结果是由 回调函数返回的东西决定的
异常穿透
我们在上面例子中 将 reject('error')的下一级then 的第二个回调去掉,这样会发生什么?
let p = new Promise((resolve, reject) => {
resolve('ok');
})
let result = p.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve('success');
})
}).then(value => {
console.log(value);
return new Promise((resolve, reject) => {
reject('error');
})
})
.then(value => {
console.log(1111);
console.log(value);
return new Promise((resolve, reject) => {
resolve('xxxx');
})
})
.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve('yyyy');
})
})
.catch(reason => {
console.log(reason);
})
console.log(result);
只要是执行器中出现
reject
函数的调用 ,下一级以及之后的then方法的第一回调就进不去了,直接会走进最后catch
方法
如何中断Promise链
在需要中断的上一级直接then方法中 return new Promise(()=>{});
let p = new Promise((resolve, reject) => {
resolve('ok');
})
p.then(value => {
console.log(value); // ok
}).then(() => {
console.log(111111); // 111111
return new Promise(()=>{});
}).then(() => {
console.log(222222);
}).then(() => {
console.log(333333);
}).catch(reason => {
console.log(reason);
})
四、async、await
解决回调地狱:
async 用于申明一个异步函数 :
异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行
异步函数的返回值特点:
- 明确有返回一个普通值,相当于
Promise.resolve
(返回值)- 明确返回一个promise,则由这个promise决定
await 关键字
- 通常await关键字后面都是跟一个Promise
- 这个promise状态变为fulfilled才会执行
await
后续的代码,所以await
后面的代码,相当于包括在.then
方法的回调中,如果状态变为rejected,你则需要在函数内部try catch
,或者进行链式调用进行.catch
操作
async function Fn() {
let p = new Promise((resolve, reject) => {
resolve('ok');
// reject('error');
})
// 捕获错误
try {
let res = await p;
console.log(res);
} catch (err) {
console.log(err);
}
}
Fn();
五、手写promise整起来😊
5.1 基础版
通过class定义一个MyPromise, new实例时传入executor执行器函数,立即执行。同时将等待状态
pending
变为fulfilled
或rejected
- 执行器有两个参数
resolve
和reject
都为函数用来改变状态,将成功的值或失败的原因保存起来。- 通过
then
方法 获取结果,在原型对象then方法中判断状态
分别调用回调函数,并传出保存好的结果。- 状态只能由
等待->成功
或者等待->失败
,需要在resolve
和reject
函数中进行判断,状态不符合程序return
阻止向下进行- try...catch 捕获立即执行的执行器函数
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 初始state状态为等待
this.status = PENDING;
// 成功之后的值
this.value = undefined;
// 失败后的原因
this.reason = undefined;
let resolve = value => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value
}
let reject = reason => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason
}
// 如果executor执行报错,直接执行reject
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
// 测试1
let p = new MyPromise((resolve, reject) => {
// resolve('成功')
reject('失败')
}).then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
})
5.2 解决异步调用resolve、reject和多实例then问题
现在基本可以实现简单的同步代码,但是当resolve在setTomeout内执行,then时status还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 代码省罗...
// 成功回调
this.successCallback = [];
// 失败回调
this.failCallback = [];
let resolve = value => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value;
// 执行成功回调
while (this.successCallback.length) this.successCallback.shift()(this.value)
}
let reject = reason => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason
// 执行失败回调
while (this.failCallback.length) this.failCallback.shift()(this.reason)
}
// 代码省罗...
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else {
// 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
this.successCallback.push(onFulfilled)
this.failCallback.push(onRejected)
}
}
}
// 测试2
let p = new MyPromise((resolve, reject) => {
setInterval(() => {
// resolve('成功。。。')
reject('失败。。。')
}, 5000)
})
p.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
p.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
5.3 解决链式调用
- 链式调用 能够.then必然上一级返回promise对象,我们需要在then方法中创建一个promise对象将原有代码包裹,并返回promise对象结果。
- 调用then方法传入的
回调函数的返回值
,需要进行判断
- 判断 result 的值是普通值还是promise对象
- 如果是普通值 直接调用resolve
- 如果是promise对象 查看promsie对象返回的结果
- 再根据promise对象返回的结果 决定调用resolve 还是调用reject
- 为了拿到创建的promise实例 需将代码变为异步代码通过setTimeout
- try...catch 捕获回调函数执行的错误
- 一旦return 返回的自己实例,我们需要解决环引用问题
处理环引用 if (promis2 === result) { return reject(new TypeError('Chaining cycle detected for promise #')) }
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 代码省罗...
}
then(onFulfilled, onRejected) {
let promis2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value)
// 判断 result 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.reason)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let result = onFulfilled(this.value)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let result = onRejected(this.reason)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promis2
}
#testPromise(promis2, result, resolve, reject) {
// 处理环引用
if (promis2 === result) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (result instanceof MyPromise) {
// promise对象
result.then(value => resolve(value), reason => reject(reason))
} else {
// 普通值
resolve(result)
}
}
}
// 测试2
let p = new MyPromise((resolve, reject) => {
resolve('成功。。。')
})
let p1 = p.then(value => {
console.log(value);
return new MyPromise((resolve, reject) => {
// resolve('hahaha')
reject('yayaya')
})
}).then(value => {
console.log(111111111);
console.log(value);
}, reason => {
console.log(222222222);
console.log(reason);
})
5.4 then参数可选
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 代码省罗...
}
then(onFulfilled, onRejected) {
onFulfilled = onFulfilled ? onFulfilled : value => value;
onRejected = onRejected ? onRejected : reason => { throw reason };
let promis2 = new MyPromise((resolve, reject) => {
// 代码省罗...
})
return promis2
}
#testPromise(promis2, result, resolve, reject) {
// 代码省罗...
}
}
// 测试2
let p = new MyPromise((resolve, reject) => {
reject('成功。。。')
})
let p1 = p.then().then(value => {
console.log(111111111);
console.log(value);
}, reason => {
console.log(222222222);
console.log(reason);
})
完整代码
catch方法通过调用then方法,将第一个参数为undefined,参数二为失败的回调传入即可
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 初始state状态为等待
this.status = PENDING;
// 成功之后的值
this.value = undefined;
// 失败后的原因
this.reason = undefined;
// 成功回调
this.successCallback = [];
// 失败回调
this.failCallback = [];
let resolve = value => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value;
// 执行成功回调
while (this.successCallback.length) this.successCallback.shift()()
}
let reject = reason => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason
// 执行失败回调
while (this.failCallback.length) this.failCallback.shift()()
}
// 如果executor执行报错,直接执行reject
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = onFulfilled ? onFulfilled : value => value;
onRejected = onRejected ? onRejected : reason => { throw reason };
let promis2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value)
// 判断 result 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.reason)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let result = onFulfilled(this.value)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let result = onRejected(this.reason)
this.#testPromise(promis2, result, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promis2
}
catch(onRejected){
return this.then(undefined, onRejected)
}
#testPromise(promis2, result, resolve, reject) {
// 处理环引用
if (promis2 === result) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (result instanceof MyPromise) {
// promise对象
result.then(value => resolve(value), reason => reject(reason))
} else {
// 普通值
resolve(result)
}
}
}
// 测试2
let p = new MyPromise((resolve, reject) => {
reject('成功。。。')
})
let p1 = p.then().then(value => {
console.log(111111111);
console.log(value);
}, reason => {
console.log(222222222);
console.log(reason);
})
手写race、all
class MyPromise {
static all(arr) {
let result = [], count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then(res => {
// 将执行结果存入result数组
result[i] = res
count++
if (count === arr.length) {
resolve(result)
}
}).catch(reason => {
reject(reason)
})
}
})
}
static race(arr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
Promise.resolve(arr[i]).then(value => {
resolve(value)
}).catch(reason => {
reject(reason)
})
}
})
}
}
// 测试代码
let p1 = new Promise((resolve, reject) => {
resolve('成功')
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1111111111')
// reject('2222222')
}, 2000)
})
let result1 = MyPromise.all([p1, 111, p2])
let result2 = MyPromise.race([p1, 111, p2])
console.log(result1);
console.log(result2);