由于 javascript 引擎是采用单线程运行机制,执行耗时过大的操作时会造成页面的阻塞,为了解决页面的阻塞问题,js 将任务分为 同步任务、异步任务,随之而来的是异步带来的执行顺序问题。
而 promise 的出现能为我们解决什么样的问题呢?
在传统的异步实现中,我们可以使用回调函数来实现异步编程,通过层层嵌套回调来满足这种异步的依赖关系,如果嵌套层数过多,可读性和可维护性都变得很差,产生所谓“回调地狱”,而Promise将回调嵌套改为链式调用,增加可读性和可维护性。
把一个函数当作参数传递,传递的是函数的定义并不会立即执行,而是在将来特定的时机再去调用,这个函数就叫做回调函数。
在定时器setTimeout以及Ajax的请求时都会用到回调函数。
setTimeout(function(){ //这个function()就是回调函数,它只有在1秒后才会执行
console.log('执行了回调函数');
},1000)
在日常的工作中我们常常会碰到一下情况:
为了处理这些情况必须使用异步的操作,而回调是处理这些情况的一种方式,所以从本质上说回调函数是异步的。
回调函数的层层嵌套被称之为 回调地狱
存在异步任务的情况下,为了确保异步任务能够按照顺序执行,我们将回调层层嵌套,此时就出现了回调地狱。回调地狱的出现会导致:
setTimeout(function () { //第一层
console.log(111);
setTimeout(function () { //第二层
console.log(222);
setTimeout(function () { //第三层
console.log(333);
}, 1000)
}, 2000)
}, 3000)
Promise 意为 ‘承诺’,意思是在未来的某个时间点承诺返回数据给你。它是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。
** 如何改变promise的状态**:promise 通过 执行器的 resolve 和 reject 两个函数来更改状态,
promise本身只是一个容器, 真正异步的是它的两个回调 resolve() 和 reject()。
promise本质 不是控制 异步代码的执行顺序(无法控制), 而是控制异步代码结果处理的顺序
promise 状态只能在 promise 内部进行操作,通过 new Promise 创建 promise 对象时,Promise 必须接受一个回调函数作为参数,我们称该函数为执行器函数,promise 的内部操作在执行器函数中执行。
回调函数 executor:(resolve, reject) => {},也叫 “执行器”,执行器 executor 是同步执行的,只有 then() 里面的回调处理才是异步的,因为它需要等待主体任务执行结束
执行器函数又包含resolve和 reject两个参数。
let promise = new Promise((resolve,reject)=>{
// 接收一个callback。参数是成功函数与失败函数
setTimeout(()=>{
let num = parseInt(Math.random()*100);
// 如果数字大于50就调用成功的函数,并且将状态变成Resolved
if(num > 50){
resolve(num);
}else{
// 否则就调用失败的函数,将状态变成Rejected
reject(num)
}
},10000)
})
除了使用 new 实例化 promise 外还可以使用 Promise.resolve(value) 返回一个 promise 对象,可以对返回值进行.then调用
Promise.resolve(11).then((value)=>{console.log(value)})
Promise.resolve() 返回任意一个非 promise 的值都会被包裹成 promise 对象
Promise.resolve().then(() => {
return new Error('error!!!')
}).then(res => {
console.log("then: ", res)
}).catch(err => {
console.log("catch: ", err)
})
// ----------------answer------------------
"then: " "Error: error!!!"
Promise.resolve() 返回任意一个非 promise 的值都会被包裹成 promise 对象,return new Error(‘error!!!’) 也被包裹成了return Promise.resolve(new Error(‘error!!!’)) 因此不会被catch捕获。
Promise.reject() 方法返回一个带有拒绝原因的Promise对象。
Promise.reject(value) 也是实例化 promise 的一种快捷方式,但是最终返回的 promise 的状态为 rejected
Promise.reject(new Error('error!!!'));
promise.then(resolved, rejected)
then 接收两个函数
then 会返回一个新的 promise 对象,将 then 的返回值包装成 Promise,所以 then 方法支持 链式调用
只有 promise 在执行了 resolve 之后,才会触发 then 回调函数的执行
promise.resolve(1).then(res=>{
console.log(res);
//在构造函数中如果你执行力resolve函数就会到这一步
},err=>{
// 执行了reject函数会到这一步
console.log(err);
})
该方法相当于 then 方法的第二个参数,指向 reject 的回调函数。会返回一个新的 promise
不过 catch 方法还有一个作用,就是在执行 resolve 回调函数时,如果出现错误,抛出异常,不会停止运行,而是进入 catch 方法中。
catch 不管在哪里都能捕获到上层未捕获的错误
能被 catch 捕获的两种情况
例如:
Promise.resolve().then(() => {
return new Error('error!!!')
}).then(res => {
console.log("then: ", res)
}).catch(err => {
console.log("catch: ", err)
})
// ----------------answer------------------
"then: " "Error: error!!!"
.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,Promise.resolve() 将抛出的对象包装成Promise.resolve(new Error(‘error!!!’)), 所以不会被后续的 .catch 捕获。
修改以上代码,使得 ‘error!!!’ 抛出时能被catch 捕获可以使用以下两种方法:
return Promise.reject(new Error('error!!!'));
// or
throw new Error('error!!!')
听说在捕获到异常后还能够继续then,那就来试试吧
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10)
}, 1000)
}).then(() => {
throw Error("1123")
}).catch((err) => {
console.log(err);
})
.then(() => {
console.log('异常捕获后可以继续.then');
})
果然如此 当第一个 .then 的异常被捕获后 catch 会返回一个新的 promise 所以 后续的then 仍能够继续执行。
Error: 1123
"异常捕获后可以继续.then"
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
Promise.resolve('1')
.then(res => {
console.log(res)
})
.finally(() => {
console.log('finally')
})
Promise.resolve('2')
.finally(() => {
console.log('finally2')
return '我是finally2返回的值'
})
.then(res => {
console.log('finally2后面的then函数', res)
})
// ----------------answer------------------
'1'
'finally2'
'finally'
'finally2后面的then函数' '2'
若是 finally 中抛出的是一个异常会怎样处理
function promise1 () {
let p = new Promise((resolve) => {
console.log('promise1');
resolve('1')
})
return p;
}
function promise2 () {
return new Promise((resolve, reject) => {
reject('error')
})
}
promise1()
.then(res => console.log(res))
.catch(err => console.log(err))
.finally(() => console.log('finally1'))
promise2()
.then(res => console.log(res))
.catch(err => console.log(err))
.finally(() => console.log('finally2'))
// ----------------answer------------------
promise1'
'1'
'error'
'finally1'
'finally2'
值穿透指的是,链式调用的参数不是函数时,会发生值穿透,就传入的非函数值忽略,传入的是之前的函数参数。
.then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传。
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// ----------------answer------------------
1
只有传入的是函数时才会传递给下一个链式调用
Promise.resolve(1)
.then(function () {
return 2
})
.then(() => { Promise.resolve(3) })
.then(console.log)
// ----------------answer------------------
undefined
此时的箭头函数没有返回值,所以 console 的结果为 undefined。
Promise.all 并发的执行一组异步任务,并且在所有异步操作执行完后才执行回调。
Promise.all() 接收一个数组,数组的每一项都是一个promise对象,会返回一个 Promise 实例。
当数组中所有的 promise 的状态都达到 resolved 的时候,all 方法的状态就会变成 resolved,
如果有一个状态变成了 rejected,那么 all 方法的状态就会变成 rejected,失败原因的是第一个失败 promise 的结果。
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log(res))
// -------------answer---------------------------
1
2
3
[1, 2, 3]
.all() 后面的 .then() 里的回调函数接收的就是所有异步操作的结果。
结果中数组的顺序与 Promise.all() 接收到的数组顺序一致
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
function runReject (x) {
const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res))
.catch(err => console.log(err))
// ----------------answer-------------
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4
catch 是会捕获最先的那个异常,在这道题目中最先的异常就是 runReject(2) 的结果,
Error: 4 将不会被 catch 捕获,而是会进入 then 的第二个参数。
另外,如果一组异步操作中有一个异常都不会进入 then() 的第一个回调函数参数中。
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res),
err => console.log(err))
Promise.race() 将一组异步任务中最先完成或失败的 Promise 返回成一个新的Promise。
如果第一个 promise 对象状态变成 resolved,那自身的状态变成了resolved;
反之第一个 promise 变成rejected,那自身状态就会变成 rejected
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 1)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 2)
})
Promise.race([p1, p2]).then((value) => {
console.log(value) // 2
})
Promise.race([p1, p2, 3]).then((value) => {
console.log(value) // 3
})
最先执行完成的,就执行相应后面的.then或者.catch。谁先以谁作为回调
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log('result: ', res))
.catch(err => console.log(err))
// ----------------answer-------------
1
'result: ' 1
2
3
它只会获取最先执行完成的那个结果,其它的异步任务虽然也会继续进行下去,不过race已经不管那些任务的结果了。
race() 后面的 .then 只会执行第一个完成的 promise 回调,其他promise 仍继续 但不会再进入到 .then
function runAsync(x) {
const p = new Promise(r =>
setTimeout(() => r(x, console.log(x)), 1000)
);
return p;
}
function runReject(x) {
const p = new Promise((res, rej) =>
setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
);
return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log("result: ", res))
.catch(err => console.log(err));
// ----------------answer-------------
0
Error: 0
1
2
3
runReject(0) 最先执行完,抛出错误 rej(Error: 0) 所以进入了 catch() 中,Promise.race().catch执行完成,将不再执行,而 runAsync(1), runAsync(2), runAsync(3) 仍继续执行。
Promise.any() 返回一组异步任务中最快的成功结果,如果全部失败就返回失败结果。
接收一个数组,数组的每一项都是一个promise对象,该方法会返回一个新的 promise,数组内的任意一个 promise 变成了resolved状态,那么由该方法所返回的 promise 就会变成resolved状态;
如果数组内的 promise 状态都是rejected,那么该方法所返回的 promise 就会变成rejected状态
all: 成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
race: 哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
any: 返回最快的成功结果,如果全部失败就返回失败结果。
async/await 是 Generator的语法糖,对异步操作的一种封装,用同步方式,执行异步操作。
async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成,用同步的方法执行异步操作。
function foo(num) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * 2)
}, 1000)
})
}
async function asyncFn() {
const num1 = await foo(1)
const num2 = await foo(num1)
const num3 = await foo(num2)
return num3
}
asyncFn().then(res => console.log(res))
async 声明一个异步函数,返回的是一个Promise对象,async 函数内部 return 的返回值,会成为 then 回调函数的参数。
async 是位于 function 前面的一个前缀,只有 async 函数中,才能使用 await。
async 执行完之后会返回一个 promise 的对象:
因此 async 修饰的函数还能继续使用 .then 进行链式调用。
async function fn () {
// return await 1234
// 等同于
return 123
}
fn().then(res => console.log(res)) // 123
await 后面跟着的是 promise 的异步操作,await 会阻塞后面的代码,直到 promise 的完成并返回其处理结果,await 后面
当 promise 的状态变为 resolved 时才会执行 await 后续的代码;
function request(num) { // 模拟接口请求
return new Promise(resolve => {
setTimeout(() => {
resolve(num * 2)
}, 1000)
})
}
async function fn () {
const res1 = await request(5)
const res2 = await request(res1)
console.log(res2) // 2秒后输出 20
}
fn()
等 request(5) 执行完之后再继续下一个 request(res1) 的请求,在 async 函数中,await 规定了异步操作只能一个一个排队执行,从而达到 **用同步方式,执行异步操作 **的效果。
如果 await 后面跟的不是 promise ,await 后面接的不是 Promise 的话,会通过 promise.resolve() 将其转换成 Promise。
function request(num) { // 去掉Promise
setTimeout(() => {
console.log(num * 2)
}, 1000)
}
async function fn() {
await request(1) // 2
await request(2) // 4
}
fn()
// 1秒后执行完 同时输出 2 4
而本案例的 setTimeout 失去了排队的效果 是因为 async/await 本身是会串行执行微任务的,
如果 await 后面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被 reject。
需要捕获 rejected 则需要在函数内部 使用 try catch 或者链式调用 .catch 操作。
使用语法:
作用:用同步方式,执行异步操作
async/await 实现同步执行异步操作
async function asyncFn() {
const num1 = await fn(1)
console.log(num1) // 2
const num2 = await fn(num1)
console.log(num2) // 4
const num3 = await fn(num2)
console.log(num3) // 8
return num3
}
const asyncRes = asyncFn()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8
const promise = new Promise((resolve, reject) => {
resolve('success')
reject('err')
})
promise.then(value => {
console.log('resolve', value)
}, reason => {
console.log('reject', reason)
})
// 输出 resolve success
通过以上例子可以分析出 promise 的实现:
promise 的状态、 promise最终结果
执行器 executor:(resolve, reject) => {}
class MyPromise{
constructor(executor){
// 1. 初始化值
this.promiseStatus = 'pending' // promiseStatus 的状态,初始化时为 pending
this.promiseResult = null // promise 的结果
executor() // 接受实例化 MyPromise 时传入的执行函数,会立即执行
}
}
在函数 resolve reject 修改 promise 的状态,并修改变化后的值,而在调用 resolve / reject 时要确定 this 的指向。
如果 执行器中 直接调用 this.resolve() 的话,resolve函数(普通函数) 中的 this 指向的是 window 或者undefined。
为了防止随着函数执行环境的变化而变化,要将resolve、reject函数中 this 指向 MyPromise 实例。
方法一: 通过 .bind(this)
class MyPromise{
constructor(executor){
// 1. 初始化值
this.promiseStatus = 'pending' // promiseStatus 的状态,初始化时为 pending
this.promiseResult = null // promise 的结果
// 2. 初始化 resolve、reject 的 this 指向
// resolve 和 reject 的 this 要指向永远指向当前的 MyPromise 实例, 否则 resolve 中的 this丢失
// 通过 bind(this) 将函数的 this 指向实例,防止随函数执行环境的变化而变化
// 或者将 resolve、 reject 函数写成箭头函数的方式
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
executor(this.resolve,this.reject) // 接受实例化 MyPromise 时传入的执行函数,会立即执行
}
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 所以在构造器中通过 bind(this) 改变 this 指向
resolve(value) {
// 如果没有 bind(this), 函数内部的 this 会丢失为 undefined
// console.log(this);
this.promiseStatus = 'resolved'
this.promiseResult = value
}
reject(reason) {
this.promiseStatus = 'rejected'
this.promiseResult = reason
}
}
方法二:采用 回调函数的形式 让 函数中的 this 指向当前实例对象
class MyPromise{
constructor(executor){
// 1. 初始化值
this.promiseStatus = 'pending' // promiseStatus 的状态,初始化时为 pending
this.promiseResult = null // promise 的结果
executor(this.resolve,this.reject) // 接受实例化 MyPromise 时传入的执行函数,会立即执行
}
// 用箭头函数就可以让this指向当前实例对象
resolve=(value)=>{
this.promiseStatus = 'resolved'
this.promiseResult = value
}
reject=(reason)=> {
this.promiseStatus = 'rejected'
this.promiseResult = reason
}
}
此时可以测试一下 MyPromise 的实现是否成功
const test1 = new MyPromise((resolve,reject)=>{
resolve('success!')
reject('error!')
})
console.log('test1 ===>> ',test1);
运行代码情况如下:
![image.png](https://img-blog.csdnimg.cn/img_convert/f2655548f142ae012f8a67cc4381a8df.png#averageHue=#212120&clientId=ua9c0d15b-857c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=135&id=u42eb4b71&margin=[object Object]&name=image.png&originHeight=203&originWidth=696&originalType=binary&ratio=1&rotation=0&showTitle=false&size=20484&status=done&style=none&taskId=u69138273-167e-4e91-926a-4520b1e43e2&title=&width=464)
而我们发现此时的状态为 rejected ,并不是第一个 resolve 成功的结果, 说明并没有保证状态变更的唯一性
为了确保 promise 的状态只变更一次,则要在 resolve 中加以限制,只有在状态为pending 是才能发生改变。
resolve(value) {
if(this.promiseStatus === 'pending'){
this.promiseStatus = 'resolved'
this.promiseResult = value
}
}
reject(reason) {
if(this.promiseStatus === 'pending'){
this.promiseStatus = 'rejected'
this.promiseResult = reason
}
}
而当执行器中 抛出异常时该如何捕获呢?
其实很简单只要在 executor 处使用 try/catch 即可,当抛出异常时使用 reject() 改变 promise 的状态
try{
executor(this.resolve,this.reject) // 接受实例化 MyPromise 时传入的执行函数,会立即执行
} catch(e){
// 当捕获到错误时执行 reject
this.reject(e)
}
then 的使用方法
promise.resolve(1).then(res=>{
console.log(res);
//在构造函数中如果你执行力resolve函数就会到这一步
},err=>{
// 执行了reject函数会到这一步
console.log(err);
})
then 的基本原理:
接收两个回调,并通过 promise 的状态选择执行的回调
then(onFulfilled, onRejected) {
// 判断状态
if (this.promiseStatus === 'resolved') {
// 调用成功回调,并且把值返回
onFulfilled(this.promiseResult);
} else if (this.promiseStatus === 'rejected') {
// 调用失败回调,并且把原因返回
onRejected(this.promiseResult);
}
}
当我们在 resolve 中遇到定时器时,then 会怎么处理呢?
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 1秒后输出 成功
}, 1000)
}).then(res => console.log('resolve',res), err => console.log('reject',err))
我们不能确保 1秒 后才执行 then 函数,但是我们可以保证 1秒 后再执行 then 里的回调。
在遇到定时器的时候我们要将 then 的两个回调函数保存起来,当 promise 的状态发生改变时再去从数组中取出对应的回调,并根据 promise 的状态判断执行哪个回调。
由于 then 支持链式调用,所以采用数组的形式来保存每一次的回调函数
class MyPromise{
constructor(executor){
// 1. 初始化值
this.promiseStatus = 'pending' // promiseStatus 的状态,初始化时为 pending
this.promiseResult = null // promise 的结果
// 2. 初始化 resolve、reject 的 this 指向
// resolve 和 reject 的 this 要指向永远指向当前的 MyPromise 实例, 否则 resolve 中的 this丢失
// 通过 bind(this) 将函数的 this 指向实例,防止随函数执行环境的变化而变化
// 或者将 resolve、 reject 函数写成箭头函数的方式
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
// then 的链式调用 并支持 定时器
this.onResolvedCallbacks = [] // 保存成功的回调
this.onRejectedCallbacks = [] // 保存失败的回调
try{
executor(this.resolve,this.reject) // 接受实例化 MyPromise 时传入的执行函数,会立即执行
} catch(e){
// 当捕获到错误时执行 reject
this.reject(e)
}
}
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 所以在构造器中通过 bind(this) 改变 this 指向
resolve(value) {
// 如果没有 bind(this), 函数内部的 this 会丢失为 undefined
// console.log(this);
// 保证只有在 pending 状态时才能发生改变
if(this.promiseStatus === 'pending'){
this.promiseStatus = 'resolved'
this.promiseResult = value
}
while (this.onResolvedCallbacks.length) {
this.onResolvedCallbacks.shift()(this.promiseResult)
}
}
reject(reason) {
// 保证只有在 pending 状态时才能发生改变
if(this.promiseStatus === 'pending'){
this.promiseStatus = 'rejected'
this.promiseResult = reason
}
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.promiseResult)
}
}
then(onFulfilled, onRejected) {
console.log(onFulfilled)
// 判断状态
if (this.promiseStatus === 'resolved') {
// 调用成功回调,并且把值返回
onFulfilled(this.promiseResult);
} else if (this.promiseStatus === 'rejected') {
// 调用失败回调,并且把原因返回
onRejected(this.promiseResult);
}else if (this.promiseStatus === 'pending') {
// 如果状态为待定状态, 表示执行器还未执行完毕,暂时保存两个回调
this.onResolvedCallbacks.push(onFulfilled.bind(this))
this.onRejectedCallbacks.push(onRejected.bind(this))
}
}
}
const test = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 1秒后输出 成功
}, 1000)
}).then(res => console.log('resolve',res), err => console.log('reject',err))
then 能实现链式调用的根本原因是它的返回对象:
// then 返回的是一个 promise 对象
then(onFulfilled, onRejected) {
var thenPromise = new MyPromise((resolve, reject) => {
// 将参数 resolvePromise 赋值为一个函数
const resolvePromise = cb => {
try {
const x = cb(this.promiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
throw new Error(err)
}
}
// 判断状态
if (this.promiseStatus === 'resolved') {
// 此时执行 resolvePromise() 并将成功的回调作为参数传入
resolvePromise(onFulfilled)
} else if (this.promiseStatus === 'rejected') {
// 执行 resolvePromise() 并将失败的回调作为参数传入
resolvePromise(onRejected)
}else if (this.promiseStatus === 'pending') {
// 如果状态为待定状态,暂时保存两个回调
this.onResolvedCallbacks.push(onFulfilled.bind(this))
this.onRejectedCallbacks.push(onRejected.bind(this))
}
})
return thenPromise
}
验证链式调用是否成功
// 链式调用 输出300
const p = new MyPromise((resolve, reject) => {
resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err))
.then(res => console.log(res), err => console.log(err))
在 js 的运行机制中 then 是微任务。当遇到微任务时,将微任务放入任务队列排队,等当前的宏任务执行完毕再去任务队列中取出微任务执行。
then 身为微任务的特质,此时在 MyPromise 中该如何实现呢? 还是得通过定时器解决该问题。
只需要让 resolvePromise 函数异步执行就可以了
then(onFulfilled, onRejected) {
var thenPromise = new MyPromise((resolve, reject) => {
// resolvePromise 接收一个 函数
const resolvePromise = cb => {
setTimeout(() => {
try {
const x = cb(this.promiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
throw new Error(err)
}
})
}
// 判断状态
if (this.promiseStatus === 'resolved') {
// 执行 resolvePromise 并将成功的回调作为参数传入
resolvePromise(onFulfilled)
} else if (this.promiseStatus === 'rejected') {
// 将失败的回调传入 resolvePromise 执行
resolvePromise(onRejected)
}else if (this.promiseStatus === 'pending') {
// 如果状态为待定状态,暂时保存两个回调
this.onResolvedCallbacks.push(onFulfilled.bind(this))
this.onRejectedCallbacks.push(onRejected.bind(this))
}
})
return thenPromise
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
// -----------------------------------------
2
1
接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
如果所有 Promise 都成功,则返回成功结果数组
如果有一个 Promise 失败,则返回这个失败结果
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = (index, value) => {
result[index] = value
count++
if (count === promises.length) resolve(result)
}
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(index, res)
}, err => reject(err))
} else {
addData(index, promise)
}
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}
static allSettled(promises) {
return new Promise((resolve, reject) => {
const res = []
let count = 0
const addData = (status, value, i) => {
res[i] = {
status,
value
}
count++
if (count === promises.length) {
resolve(res)
}
}
promises.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData('fulfilled', res, i)
}, err => {
addData('rejected', err, i)
})
} else {
addData('fulfilled', promise, i)
}
})
})
}
any 与 all 相反
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((promise) => {
promise.then(val => {
resolve(val)
}, err => {
count++
if (count === promises.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
}
上文我们说到 async/await 是 基于 generator 实现的语法糖,那么先来看一看究竟什么是 generator:
Generator 函数是 ES6 提供的一种异步编程解决方案,可以把 Generator 理解成一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象该对象可以依次遍历 Generator 内部的每一个状态,为改变执行流程提供了可能,从而为异步编程提供解决方案
generator 与普通函数的区别是多了一个 * 号,并且只有在 generator 函数中才能使用 yield,当执行到 yield 时generator 的执行流会被挂起,通过 next() 方法能让 generator 切换到下一个状态,next() 执行后会返回一个对象,其中包含两个属性 value 和 done
function* gen() {
yield 1
yield 2
yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
当 generator 暂停点 yield 后接的是一个函数时,**马上执行该函数并将函数的返回值 **作为 yield 的对象 value 值
当 yield 后函数的返回值为 Promise 时,会将当前状态为 pending 的 promise 对象 作为 yield 的 value 值
function fn(num) {
return new Promise(resolve => {
console.log('执行resolve')
setTimeout(() => {
console.log('执行setTimeout')
resolve(num)
}, 1000)
})
}
function* gen() {
yield fn(1)
yield fn(2)
return 3
}
const g = gen()
console.log(g.next().value)
console.log(g.next().value)
console.log(g.next())
在执行 gen.next() 时会在将 yeild 之后的函数执行的返回值作为暂停点对象的value值
所以此时的执行结果为:
![image.png](https://img-blog.csdnimg.cn/img_convert/d7a8293a2fb1cee3481f8630d2059b71.png#averageHue=#fdfdfc&clientId=u81e94d19-a8f9-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=149&id=ud376af1e&margin=[object Object]&name=image.png&originHeight=143&originWidth=289&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7502&status=done&style=none&taskId=ub9d976c1-c6cb-402d-be97-7e912d8a85b&title=&width=300.20001220703125)
fn() 返回了一个promise,而 promise 的状态在定时器中才发生变更,所以前两个的 value 都是实例化时 promise 的状态 pending
若此时没有l定时器的干扰,promise 的状态会发生什么样的而变化呢?
function fn(num) {
return new Promise(resolve => {
console.log('执行resolve')
resolve(num)
})
}
function* gen() {
yield fn(1)
yield fn(2)
return 3
}
const g = gen()
console.log(g.next().value)
console.log(g.next().value)
console.log(g.next())
实例化 promise 时会执行resolve 回调,此时 next 返回的 promise 状态会从 pending 转变为 fulfilled (resolved)
![image.png](https://img-blog.csdnimg.cn/img_convert/0fed569e9f5456b832988c2ac31e62c5.png#averageHue=#fefdfc&clientId=u81e94d19-a8f9-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=122&id=ub73475f3&margin=[object Object]&name=image.png&originHeight=127&originWidth=333&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7218&status=done&style=none&taskId=u940c16c4-3fe5-448b-9e32-1ffcbd40fc2&title=&width=320.3999938964844)
generate 可以用 next() 传递参数,通过 yield 来接收传递的值:
function* gen() {
const num1 = yield 1
console.log(num1)
const num2 = yield 2
console.log(num2)
return 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next(11111))
// 11111
// { value: 2, done: false }
console.log(g.next(22222))
// 22222
// { value: 3, done: true }
此时输出的结果为
{value: 1, done: false} // 第一个 next 返回的 yield 1 的对象
1 11111 // 第二个 next 执行时传入 11111 被第一个 yield 接收并赋值给num1, 此时 num1 = 11111
{value: 2, done: false} // yield 2 时 返回的对象
22222 // 第三个 next 传入 22222时 yield 接收参数并赋值给 num2, num2 = 22222
{value: 3, done: true} // generator return 3
Promise + next 传参
当我们 Promise 与 next 传参相结合是会发生什么情况呢?
function fn(nums) {
return new Promise(resolve => {
setTimeout(() => {
resolve(nums * 2)
}, 1000)
})
}
function* gen() {
const num1 = yield fn(1) // 调用 g.next(res1)时 传入的参数会被 yield 接收 并赋值给 num1
const num2 = yield fn(num1)
const num3 = yield fn(num2)
return num3
}
const g = gen()
const next1 = g.next()
next1.value.then(res1 => {
console.log(next1) // 1秒后同时输出 { value: Promise { 2 }, done: false }
console.log(res1) // 1秒后同时输出 2
const next2 = g.next(res1) // 传入上次的res1 2
next2.value.then(res2 => {
console.log(next2) // 2秒后同时输出 { value: Promise { 4 }, done: false }
console.log(res2) // 2秒后同时输出 4
const next3 = g.next(res2) // 传入上次的res2
next3.value.then(res3 => {
console.log(next3) // 3秒后同时输出 { value: Promise { 8 }, done: false }
console.log(res3) // 3秒后同时输出 8
// 传入上次的res3
console.log(g.next(res3)) // 3秒后同时输出 { value: 8, done: true }
})
})
})
async/await 的实现原理:
generator 函数的 Promise+next 传参,就很像 async/await 了,区别在于
function generatorToAsync(generatorFn) {
return function() {
const gen = generatorFn.apply(this, arguments) // gen有可能传参
// 返回一个Promise
return new Promise((resolve, reject) => {
function go(key, arg) {
let res
try {
res = gen[key](arg) // 执行 next(), 获取执行返回的对象,有可能是reject状态的Promise
} catch (error) {
return reject(error) // 报错的话会走catch,直接reject
}
// 解构获得value和done
const { value, done } = res
if (done) {
// 如果done为true,说明走完了,进行resolve(value)
return resolve(value)
} else {
// 如果done为false,说明没走完,还得继续走
// value有可能是:常量,Promise,Promise有可能是成功或者失败
return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))
}
}
go("next") // 第一次执行
})
}
}
使用 generatorToAsync函数 的版本
function* gen() {
const num1 = yield fn(1)
console.log(num1) // 2
const num2 = yield fn(num1)
console.log(num2) // 4
const num3 = yield fn(num2)
console.log(num3) // 8
return num3
}
const genToAsync = generatorToAsync(gen)
const asyncRes = genToAsync()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8