学习关键语句:
Promise
promise学习
promise.all
promise.race
promise.resolve
promise 是前端绕不开的东西 , 所以我们一定要好好学习 , 写这篇文章的目的是加深对 promise 的学习和使用程度
首先创建一个文件夹 , 里面新建一个 index.html 和 index.js
我们在浏览器环境观看 promise 的使用效果
我们要先看一看这个 promise 到底是啥
let o1 = new Object()
console.log(o1)
let p1 = new Promise((resolve, reject) => { })
console.log(p1)
我们可以看到 promise 比正常的对象要多了两个东西 , 一个是 PromiseState , 值为 pending ; 一个是 PromiseResult , 值为 undefined
我们从实例化方式和打印对象中可以看出 , promise 是一个构造函数或者说类 , 看上去只不过是在实例化时传入一个函数而已 , 那么我们继续看一看一个简单的 promsie 例子后 , 我们再看这个 promise 变成什么了
let x = 1
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x)
} else {
reject(x)
}
})
p1.then(value => {
console.log('value:' + value) // value:1
}, reason => {
console.log('reason:' + reason)
})
console.log(p1)
我们看到 PromiseState 变成了 fulfilled 而 PromiseResult 变成了 1
这好像没什么大不了的嘛 , 乍一看就跟乍一看一样 , 好像理所当然应该是这样的
接下来我们将 prototype 点开 ,看一看原型上有哪些方法
原型上的方法不多嗷 , 我们也只需要关注 catch finally 和 then 方法就够了
ok 那么我们已经完全看到了 promise 实例的相关内容 , 接下来我们来看一看 promise 构造器上有什么内容
点开 constructor
构造器上的方法我们需要关注的就是 all allSettled any race reject 和 resolve 方法
好啦 , 这下 promise 已经被你完全看完了 , 到这里大致混个眼熟就 ok , 接下来才开始学习
可能你会有所疑惑 , 所以接下来介绍一下基础知识
一、PromiseState
一个 promise 有三个状态 , 分别是 pending , fulfilled 和 rejected
特别注意:
二、PromiseResult
顾名思义 , 其实指的就是一个 promise 的结果值
三、promise中的两个参数函数
我们看以下代码
let x = 1;
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x);
} else {
reject('出错了');
}
});
p1 是我们新实例化的一个 promise 对象 , 和正常实例化一样的是 , 我们在初始化时传递了一个参数进去 , 只不过这个参数是一个函数 , 喏就是下面这个函数 , 是不是这样看起来就好很多
(resolve, reject) => {
if (x < 2) {
resolve(x);
} else {
reject('出错了');
}
}
这个函数一共有两个参数 , resolve 和 reject , 而这两个参数恰好又是两个函数 , 只不过这两个函数不是我们定义的 , 我们在这里写的 resolve 和 reject 只是两个形参 , 你可以随便改名 , 叫啥都行
在 promise 的构造器中会自动调用我们传入的这个函数 , 同时会将真正的实参函数 resolve 和 reject 传入进来
而在我们的函数体中 , 当我们想要 履行承诺 时就调用 resolve 方法 , 并且将想要传递的值作为参数放入 ; 想要 拒绝承诺 时就调用 reject 方法 , 并且将拒绝的理由作为参数放入 ; 而无论是成功还是失败 , 我们放入的参数都会成为最终的 PromiseResult
同时 , 刚刚才学过的 PromiseState 知识告诉我们 , 我们调用 resolve 方法后状态就会从 pending 变成 fulfilled , 调用 reject 方法后状态就会从 pending 变成 rejected
你可以看到在我们上面的代码中 , x = 1 , 所以 x < 2 是成立的我们想要的结果 , 那么我们就 履行承诺 调用resolve 方法并且把 x 传入同时状态变成了 fulfilled , 否则呢我们就 拒绝承诺 调用 reject 方法并且把我们的拒绝理由 出错了 传入同时状态变成了 rejected
总的来说就是 , 这两个参数函数和我们无关 , 我们只要达到目的的时候调用一下就可以了
好了 , 基本上已经了解的差不多了 , 接下来我们说说原型上的方法
首先我们要先弄清楚什么叫履行承诺 , 什么叫拒绝承诺
第一步是我给出承诺 , 如果我履行就干嘛干嘛 , 如果我拒绝就怎样怎样
第二步是你看我的行为 , 我到底是履行了还是拒绝了 , 我履行了你就怎样怎样 , 我拒绝了你就干嘛干嘛
其实就是一个根据上一个行为做出下一个行为
我们来看以下代码
let x = 1
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x)
} else {
reject('出错了')
}
})
p1.then(value => {
console.log('value:' + value)
}, reason => {
console.log('reason:' + reason)
})
在这段代码的使用过程中呢 , 我们可以看到 then() 方法接收两个参数 , 而第一个参数就是成功的回调 , 当一个 promise 使用 resolve 方法时 , then() 方法就会进入第一个参数函数 , 同时将 resolve 方法中的参数传入 ;
当一个 promise 使用 reject 方法时 , then() 方法就会进入第二个参数函数 , 同时将 reject 方法中的参数传入
上面的代码中 , 我们调用的是 resolve 方法 , 所以 x 作为参数传到了第一个函数中替代了 value 形参 , 所以就打印了 value: 1
同样的如果将上面代码中的 x 修改为 10 , 就会调用 reject 方法 , 出错了 三个字就会作为参数传到第二个函数中替代 reason 形参 , 打印出 reason: 出错了
这就是 then() 方法了 , 是不是又简单又好用
我们已经了解到了简单好用的 then() 方法 , 但是我还是觉得一个参数中写两个函数很难受 , 别问问就是难受 , 那怎么办呢
那当然就看看 catch() 方法了 , 我们来看以下代码
let x = 10
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x)
} else {
reject('出错了')
}
})
p1.then(value => {
console.log('value:' + value)
}).catch(err => {
console.log('err:' + err)
})
可以看到 , 在 then() 中我只写了一个参数函数 , 那么按理说我们应该获取不到拒绝承诺的结果的 , 但是我们在后面又补上了一个 catch() 方法 , 同时 catch() 方法中的参数也是一个函数 , 所以 catch() 方法就是专门用来捕获拒绝的承诺 , 这样相比在 then() 中写两个函数要清晰明了很多 , 我们看看控制台中打印了什么
当然了 , 你可能会问 , 那我都写呗 , 当然没问题啦 , 只不过都写的话 , catch() 方法就会失效
let x = 10
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x)
} else {
reject('出错了')
}
})
p1.then(value => {
console.log('value:' + value)
}, reason => {
console.log('reason:' + reason)
}).catch(err => {
console.log('err:' + err)
})
所以可以认为 catch() 方法是 then() 方法的语法糖 , 当正主出来的时候 , 替身自然就要下场了
注意
你可能已经注意到了 , 在上面的代码中 , 我们是在 then() 方法的末尾直接使用点语法调用的 catch() 方法 , 因此我们意识到每个方法都会返回一个新的 promise , 这样才能达到不断链式调用的效果
顾名思义啊 , 这个 finally() 方法呢就是最后执行的方法 , 而且是无论如何都会执行的方法 , 不接收参数
let x = 10
let p1 = new Promise((resolve, reject) => {
if (x < 2) {
resolve(x)
} else {
reject('出错了')
}
})
p1.then(value => {
console.log('value:' + value)
}).catch(err => {
console.log('err:' + err)
}).finally(a => {
console.log('finally:', a)
})
我们看到 finally 打印出来的 a 是 undefined , 所以 finally() 方法是没有参数的 , 而且一定会执行
注意
finally() 方法也会返回一个新的 promise , 所以同样可以链式调用p1.then(value => { console.log('value:' + value) }).finally(a => { console.log('finally:', a) }).catch(err => { console.log('err:' + err) })
前面需要学习的原型方法就是那三个啦 , 接下来学习一下静态方法 , 也就是使用 static 声明在类中的方法
静态方法的话呢我觉得还是从 Promise.resolve() 方法开始学起最为简单 , 因为他的功能最简单
效果 : 将参数包装为 promise 对象
当然了 , 这里的参数需要分情况处理
参数为 promise 对象
如果参数本身就已经是一个 promise 对象了 , 那就原封不动的返回这个 promise 对象
let p1 = new Promise((resolve, reject) => { });
let p2 = Promise.resolve(p1);
console.log(p1 === p2); // true
参数是普通数据
参数是数字 , 字符串 , 布尔值 , undefined , null , 数组 , 对象 , Set , Map 时则返回一个新的Promise对象,且 PromiseState 状态为 fulfilled , PromiseResult 值为参数值
console.log(Promise.resolve(123))
console.log(Promise.resolve('shaoyahu'))
console.log(Promise.resolve(false))
console.log(Promise.resolve(undefined))
console.log(Promise.resolve(null))
console.log(Promise.resolve([1, 3]))
console.log(Promise.resolve({ name: 'shaoyahu' }))
console.log(Promise.resolve(new Set([1, 2])))
console.log(Promise.resolve(new Map([[1, 1], [2, 2]])))
没有参数
没有参数就直接返回一个空值的 promise
let p1 = Promise.resolve()
let p2 = Promise.resolve(undefined)
console.log(p1)
console.log(p2)
console.log(p1 === p2)
用法和 Promise.resolve() 方法一样 , 只不过返回值的状态为 rejected , 其他没有区别
效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 而这个新的 promise 只有 所有的 promise 全部状态变为 fulfilled 或者 任意一个 promise 状态变为 rejected 时才会从 pending 状态变为相应的状态
all() 方法接收的参数是一个数组 , 将所有的 promise 对象放在数组中 , 如果出现不是 promise 对象的参数 , 则默认使用 Promise.resolve() 方法进行包裹
简单来说就是所有的 promise 成功就成功 , 有一个失败就失败 , 我们看以下代码
let p1 = new Promise((resolve, reject) => {
resolve('p1')
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000);
})
let p3 = new Promise((resolve, reject) => {
resolve('p3')
})
let p = Promise.all([p1, p2, p3, 123])
console.log(p)
p.then(value => {
console.log(value)
console.log(p)
})
我们来看看结果 , 很显然 , 一开始 p 是处于 pending 状态的 , 等数组中所有的 promise 都返回成功之后才改变为 fulfilled 状态 , 并且返回值是每个 promise 返回值组成的数组
如果有一个 promise 失败了 , 那么返回的就会是 rejected 状态的 p , 同时返回值是失败的那个 promise 的失败原因
这个方法和 all() 方法其实差不多 , 只不过你看名字是 race , 竞速嘿嘿
效果 : 将多个 promise 实例 , 包装成一个新的 promise 实例 , 一旦迭代器中的某个 promise 成功或者失败 , 那么新的 promise 就会立即成功或者失败
简单来说就是只取返回最快的值,无论是成功还是失败 , 我们看以下代码
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1500);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000);
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3')
}, 1000);
})
let p = Promise.race([p1, p2, p3])
console.log(p)
p.then(value => {
console.log(value)
console.log(p)
})
我们从代码中可以看出 , 最快的应该是 p3 , 所以结果肯定就是 p3 了
效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 原有的 promise 中一旦有一个成功了 , 就返回成功的那个 promise 值, 并且新的 promise 状态改为 fulfilled
简单来说 , 就是 Promise.all() 方法的眼瞎版 , 不用看失败的只看成功的 , 是 Promise.race() 方法的偏心版 , 我只拿第一个 , 但是我只看成功的 , 我们看以下代码
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1500);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000);
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p3')
}, 1000);
})
let p = Promise.any([p1, p2, p3])
console.log(p)
p.then(value => {
console.log(value)
console.log(p)
})
p3 是最快的 , 但是它是失败的 , 所以返回值是最快成功的 p1
效果 : 对多个原有的 promise 包装进一个新的 promise 中 , 当所有的 promise 状态都发生改变时 , 新的 promise 状态改为 fulfilled , 并且返回值是原有 promise 返回值组成的数组加工后的值
简单来说 , 就是 Promise.all() 方法忽略了失败 , 将所有的结果都返回过来 , 我们看以下代码
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1500);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000);
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p3')
}, 1000);
})
let p = Promise.allSettled([p1, p2, p3])
console.log(p)
p.then(value => {
console.log(value)
console.log(p)
})
我们直接看返回数据 , 你一下就会懂了 , 很清晰的
Promise 作为前端必须要学习的一个重要知识点 , 想必大家上面的内容其实都会吧 , 我真是班门弄斧啊 , 那么之后 , 我们来重写一个 promise 类试试看吧
学到这里 , 终于可以松一口气了 , 怎么样 , 是不是已经完全掌握了呢 ?
其实还是挺有难度的 , 难度在真正用起来可能会比较绕 , 但是方法就这些 , 除非你只是在调用接口
如果你跟着做遇到了什么问题无法实现 , 请在评论区或者私信告诉我 , 我发动网友给你解答