这个前端学习笔记是学习gitchat上的一个课程,这个课程的质量非常好,价格也不贵,非常时候前端入门的小伙伴们进阶。
笔记不会涉及很多,主要是提取一些知识点,详细的大家最好去过一遍教程,相信你一定会有很大的收获
我们看下微信小程序的请求API
wx.request({
url: 'test.php', // 仅为示例,并非真实的接口地址
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
console.log(res.data)
}
})
如果需要通过请求结果再次请求,就会出现回调地狱
wx.request({
url: 'test.php', // 仅为示例,并非真实的接口地址
//...
success(res) {
wx.request({
url: 'test.php', // 仅为示例,并非真实的接口地址
//...
success(res) {
console.log(res.data)
}
})
}
})
我们现在使用promise来改善这种写法
const require = (url, data, method)=>{
return new Promise((res,rej)=>{
wx.request({
url,
data,
method,
head:{
},
success:(data)=>{
res(data)
},
fail:(error)=>{
rej(error)
}
})
}})
}
// 多层请求嵌套
require('test.php', {
data:'1'}, 'get').then((data)=>{
return require('test.php', data, 'get')
}).then((data)=>{
return require('test.php', data, 'get')
})// 可以继续嵌套
从上面可知,promise 是一个构造函数,使用的是promise的实例。
接受一个函数参数,而这个函数的参数有2个,分别代表这成功回调和失败回调
而then
接受的2个函数参数分别对应着上面的2个函数参数
function Promise(fn) {
// fn(onfulfilled, onrejected){}
}
Promise.prototype.then = function(onfulfilled, onrejected) {
}
在promise构造函数的时候,通过onfulfilled, onrejected
将任务时间放置到异步队列,只要有一个触发,另外一个就没有用了,也是promise的唯一性,所以需要内部有2个值保存着这个函数。
promise还设置了一个状态(pending,fulfilled,rejected
)
function Promise(fn) {
const self = this
this.status = 'pending'
this.value = null// 成功回调的值
this.reason = null// 失败回调的原因
function resolve(value) {
self.value = value
}
function reject(reason) {
self.reason = reason
}
fn(resolve, reject)// 将then设置的函数回调传给fn
}
// 为了保证 onfulfilled、onrejected 能够强健执行,我们为其设置了默认值,其默认值为一个函数元(Function.prototype)。
Promise.prototype.then = function(onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value)
onrejected(this.reason)
}
从promise题目来看看状态的作用
let promise = new Promise((resolve, reject) => {
resolve('data')
reject('error')
})
promise.then(data => {
console.log(data)
}, error => {
console.log(error)
})
只会输出data
,因为只能执行一个回调,虽然函数里调用了2个回调参数,但是promise只会执行第一个,这是因为promise有一个状态机制。
所以我们需要在内部加一个状态,来控制回调的执行。
function Promise(fn) {
this.status = 'pending'// 初始状态
this.value = null
this.reason = null
const resolve = value => {
if (this.status === 'pending') {
// 如果用户执行了 resolve 则改变状态为fulfilled
this.value = value
this.status = 'fulfilled'
}
}
const reject = reason => {
if (this.status === 'pending') {
// 如果用户执行了 reject 则改变状态为rejected
this.reason = reason
this.status = 'rejected'
}
}
fn(resolve, reject)
}
// 参数加多层判断,如果传入不是函数的话,将它转换成函数
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
// 避免在加载的时候就执行成功回调
onfulfilled(this.value)
}
if (this.status === 'rejected') {
// 避免在加载的时候就执行失败回调
onrejected(this.reason)
}
}
从例子看看promise如何进行异步处理
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data')
}, 2000)
})
promise.then(data => {
console.log(data)
})
// 2秒之后输出data
由于我们2秒之后才调用resolve
,所以promise2秒内状态都没有发生变化。虽然使用then,但是状态一直是pending
,所以无法执行onfulfilled
,所以我们需要在内部中在promise改变状态的时候,自己调用onfulfilled
function Promise(fn) {
this.status = 'pending'
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
// 等待执行
this.onFulfilledFunc(this.value)
}
}
const reject = reason => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
// 等待执行
this.onRejectedFunc(this.reason)
}
}
fn(resolve, reject)
}
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
onfulfilled(this.value)
}
if (this.status === 'rejected') {
onrejected(this.reason)
}
if (this.status === 'pending') {
// 保存回调函数
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
但是如果不是异步调用resolve的话,就会导致执行顺序和原本promise的不一样
let promise = new Promise((resolve, reject) => {
resolve('data')
})
promise.then(data => {
console.log(data)
})
console.log(1)
正常会输出 1然后输出 data,但是我们实现的没有考虑这个情况,输出是data然后是1
所以我们在promise内部同样需要进行一个异步处理。
我们要知道,promise异步是微任务的,可以使用nodejs 的nextTick
或则mutationObserve
来模拟,我们这里使用setTimeout
来实现。
const resolve = value => {
if (value instanceof Promise) {
// 传入的如果是promise 链模式
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc(this.value)
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc(this.reason)
}
})
}
看一下到目前为止的实现代码:
function Promise(executor) {
this.status = 'pending'
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc(this.value)
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc(this.reason)
}
})
}
executor(resolve, reject)
}
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
onfulfilled(this.value)
}
if (this.status === 'rejected') {
onrejected(this.reason)
}
if (this.status === 'pending') {
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
这样,promise的输出就能够异步进行了。
因为promise是支持链式添加then,所以我们需要返回一个promise作为结果。
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data')
}, 2000)
})
promise.then(data => {
console.log(`1: ${
data}`)
})
promise.then(data => {
console.log(`2: ${
data}`)
})
输出情况
//1: data
//2: data
这就需要将内部保存的onFulfilledFunc
变成一个栈保存,使用数组即可。
function Promise(fn) {
this.status = 'pending'
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledArray.forEach(func => {
// 按顺序调用
func(value)
})
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedArray.forEach(func => {
// 按顺序调用
func(reason)
})
}
})
}
fn(resolve, reject)
}
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
onfulfilled(this.value)
}
if (this.status === 'rejected') {
onrejected(this.reason)
}
if (this.status === 'pending') {
// 栈保存
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}
还有一个细节,如果传入的函数有语法错误,promise的状态会变成rejected
我们只需要使用try-catch
来执行函数,如果出错就调用rejected
try {
fn(resolve, reject)
} catch(e) {
reject(e)
}
最终版本:
function Promise(executor) {
this.status = 'pending'
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledArray.forEach(func => {
func(value)
})
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedArray.forEach(func => {
func(reason)
})
}
})
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
onfulfilled(this.value)
}
if (this.status === 'rejected') {
onrejected(this.reason)
}
if (this.status === 'pending') {
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}function Promise(executor) {
this.status = 'pending'
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledArray.forEach(func => {
func(value)
})
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedArray.forEach(func => {
func(reason)
})
}
})
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
Promise.prototype.then = function(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error}
if (this.status === 'fulfilled') {
onfulfilled(this.value)
}
if (this.status === 'rejected') {
onrejected(this.reason)
}
if (this.status === 'pending') {
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
promise.then(data => {
console.log(data)
return `${
data} next then`
})
.then(data => {
console.log(data)
})
这段代码执行后,将会在 2 秒后输出:rex
,紧接着输出:rex next then。
我们分解上一个例子,第一次绑定的then是
data => {
console.log(data)
return `${
data} next then`
}
我们可以在then方法里 添加一个返回新的promise
Promise.prototype.then = function(onfulfilled, onrejected) {
// promise2 将作为 then 方法的返回值
let promise2
if (this.status === 'fulfilled') {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
// 这个新的 promise2 resolved 的值为 onfulfilled 的执行结果
let result = onfulfilled(this.value)
resolve(result)
}
catch(e) {
reject(e)
}
})
})
}
if (this.status === 'rejected') {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
// 这个新的 promise2 reject 的值为 onrejected 的执行结果
let result = onrejected(this.value)
resolve(result)
}
catch(e) {
reject(e)
}
})
})
}
if (this.status === 'pending') {
return promise2 = new Promise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let result = onfulfilled(this.value)
resolve(result)
}
catch(e) {
reject(e)
}
})
this.onRejectedArray.push(() => {
try {
let result = onrejected(this.reason)
resolve(result)
}
catch(e) {
reject(e)
}
})
})
}
}
前面2中状态都好理解,pendding
这个状态是如何让promise能够执行后续的then?
返回的新的promise2
其实将promise2
的then
执行函数保存在promise1
的异步任务里去了,当promise1
执行结束的时候,promise2
的then
开始执行,添加到异步任务去,这样就能够执行第二个then
,同理promise2
调用then
会返回第三个的promise
如果用户显式返回promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
promise.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${
data} next then`)
}, 4000)
})
})
.then(data => {
console.log(data)
})
我们需要将result返回的结果进行判断,一种是普通值,一种是promise的实例
为此我们抽象出 resolvePromise 方法进行统一处理。改动已有实现为
const resolvePromise = (promise2, result, resolve, reject) => {
}
这个函数接受四个参数:
const resolvePromise = (promise2, result, resolve, reject) => {
// 当 result 和 promise2 相等时,也就是说 onfulfilled 返回 promise2 时,进行 reject
// 避免死循环
if (result === promise2) {
reject(new TypeError('error due to circular reference'))
}
// 是否已经执行过 onfulfilled 或者 onrejected
let consumed = false
let thenable
if (result instanceof Promise) {
if (result.status === 'pending') {
result.then(function(data) {
resolvePromise(promise2, data, resolve, reject)
}, reject)
} else {
result.then(resolve, reject)
}
return
}
let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && (target !== null)
// 如果返回的是疑似 Promise 类型
if (isComplexResult(result)) {
try {
thenable = result.then
// 如果返回的是 Promise 类型,具有 then 方法
if (typeof thenable === 'function') {
thenable.call(result, function(data) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise2, data, resolve, reject)
}, function(error) {
if (consumed) {
return
}
consumed = true
return reject(error)
})
}
else {
resolve(result)
}
} catch(e) {
if (consumed) {
return
}
consumed = true
return reject(e)
}
}
else {
resolve(result)
}
}
对于onfulfilled
函数返回的结果result
:如果result
非 Promise 实例,非对象,非函数类型,是一个普通值的话(上述代码中isComplexResult
函数进行判断),我们直接将 promise2 以该值 resolve
掉。
对于onfulfilled
函数返回的结果result
:如果result
含有then
属性方法,我们称该属性方法为 thenable
,说明result
是一个 Promise 实例,我们执行该实例的then
方法(既thenable
),此时的返回结果有可能又是一个 Promise 实例类型,也可能是一个普通值,因此还要递归调用resolvePromise
。
从例子看为什么要递归调用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
promise.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${
data} next then`)
}, 4000)
})
.then(data => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${
data} next then`)
}, 4000)
})
})
})
.then(data => {
console.log(data)
})
该段代码将会在 2 秒是输出:rex,10 秒时输出:rex next then next then。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
promise.then(null)
.then(data => {
console.log(data)
})
2秒后输出rex
,这就是 Promise 穿透现象:
给 .then() 函数传递非函数值作为其参数时,实际上会被解析成 .then(null),这时候的表现应该是:上一个 promise 对象的结果进行“穿透”,如果在后面链式调用仍存在第二个 .then() 函数时,将会获取被穿透下来的结果。
其实很简单,并且我们已经做到了。想想在 then() 方法的实现中:我们已经对 onfulfilled 和 onrejected 函数加上判断:
Promise.prototype.then = function(onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => {
throw error }
// ...
}
如果 onfulfilled 不是函数类型,则给一个默认值,该默认值是返回其参数的函数。onrejected 函数同理。这段逻辑,就是起到了实现“穿透”的作用。
Promise.prototype.catch 可以进行异常捕获,它的典型用法:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('rex error')
}, 2000)
})
promise1.then(data => {
console.log(data)
}).catch(error => {
console.log(error)
})
会在 2 秒后输出:rex error。
实现:
Promise.prototype.catch = function(catchFunc) {
return this.then(null, catchFunc)
}
Promise.resolve(value) 方法返回一个以给定值解析后的 Promise 实例对象。
Promise.resolve('data').then(data => {
console.log(data)
})
console.log(1)
先输出 1 再输出 data。
实现
Promise.resolve = function(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('rex')
}, 2000)
})
Promise.all([promise1, promise2]).then(data => {
console.log(data)
})
将在 2 秒后输出:[“rex”, “rex”]。
Promise.all = function(promiseArray) {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array!')
}
return new Promise((resolve, reject) => {
try {
let resultArray = []
const length = promiseArray.length
for (let i = 0; i <length; i++) {
promiseArray[i].then(data => {
resultArray.push(data)// 结果保存到数组
// 全部异步执行完毕,返回最终结果
if (resultArray.length === length) {
resolve(resultArray)
}
}, reject)
}
}
catch(e) {
reject(e)
}
})
}
这里用闭包保存一个length长度,没成功执行一个promise就加1,这里没有加1,但是是使用result结果长度来判断,如果长度一样则代表所有异步已经全部返回,知道所有异步执行完成,就执行promise.all的异步任务,把所有结果当作resolve的参数
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('lucas1')
}, 2000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('lucas2')
}, 4000)
})
Promise.race([promise1, promise2]).then(data => {
console.log(data)
})
将会在 2 秒后输出:lucas1,实现 Promise.race 为:
Promise.race = function(promiseArray) {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array!')
}
return new Promise((resolve, reject) => {
try {
const length = promiseArray.length
for (let i = 0; i <length; i++) {
promiseArray[i].then(resolve, reject)// 第一个执行成功的会调用resolve
}
}
catch(e) {
reject(e)
}
})
}
这里使用 for 循环同步执行 promiseArray 数组中的所有 promise 实例 then 方法,第一个 resolve 的实例直接会触发新 Promise(代码中新 new 出来的) 实例的 resolve 方法。后续调用resolve就会失效了。