catch 方法实现
catch 方法的使用
// 当then为空
let p1 = new Promise((resolve, reject) => {
reject('失败')
})
p1.then().catch(err => {
console.log('failed', err)
})
// failed 失败
当 then 为空时,错误被持续抛出到最后一个 then 的 onRejected 函数中,因此 catch 可以理解为then(null, err=>{}) 注意 catch 只有一个 onRejected 函数,onFufilled 为 null。
源码实现
class Promise {
...
catch(errCallback) {
return this.then(null, errCallback)
}
}
Promise.resolve,Promise.reject
Promise.resolve 的使用
// 快速创建一个成功的promise
Promise.resolve(123).then(data => {
console.log(data)
})
// 上述写法等同下面
new Promise((resolve, reject) => {
resolve(123)
}).then(data => {
console.log(data)
})
// 特殊情况是,Promise内再套Promise
new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(123)
})
)
}).then(data => {
console.log(data)
})
源码实现
class Promise {
...
let resolve = (value) => {
// 如果当前value是promise实例,递归调用then
if(value instanceof Promise) {
return value.then(resolve, reject)
}
if(this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// Promise.resolve是静态方法
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
// Promise.reject是静态方法,reject直接就是抛错,不用递归
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
}
finally 方法实现
划重点
finally表示不是最终的意思,而是无论如何都会执行的意思
如果前面是成功,那么 finally 之后继续.then获取成功的数据
如果前面是失败,那么 finally 之后继续.catch捕获失败的错误
如果 finally 返回一个 promise,会等待这个 promise 也执行完毕;如果是失败的 promise,会用它的失败原因传给下一个人
finally 的使用
Promise.resolve(123)
.finally(() => {
console.log('finally')
})
.then(data => {
console.log('success', data)
})
// 输出:finally success 123
Promise.reject(123)
.finally(() => {
console.log('finally')
})
.catch(err => {
console.log('failed', err)
})
// 输出: finally failed 123
Promise.resolve(123)
.finally(() => {
console.log('finally')
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('ok')
reject('error')
}, 1000)
})
})
.then(data => {
console.log('success', data)
})
.catch(err => {
console.log('failed', err)
})
// 输出failed error
分析 finally
finally 实际上就是返回了一个then,只是传入的回调函数,无论如何都会被执行
finally 之后可以继续.then,所以不管传入的函数是不是 promise,都得包装成 promise
如果上一个状态是resolve,那么如果 finally 内部 promise 返回的也是 resolve,则忽略,沿用上一轮的 resolve 值
简而言之,如果是 resolve 状态,value 沿用上一次的继续往下传,如果是 reject 状态,不必沿用上一轮的值。
源码实现
class Promise {
...
finally(callback) {
return this.then(value => {
return Promise.resolve(callback()).then(() => value)
},reason => {
return Promise.resolve(callback()).then(() => { throw reason })
})
}
// Promise.resolve是静态方法
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
}
promisify 的实现
在 nodeJs 中,绝大多数 API 都是采用异步回调的方式,嵌套层级深,比如读取文件,我们每次封装都需要像如下代码这样,套一层 promise,添加错误判断,返回读取的正确值。
const fs = require('fs')
const path = require('path')
function readFile(filename) {
return new Promise((resolve, reject) => {
fs.readFile(path.resolve(__dirname, filename), 'utf8', function (err, data) {
if (err) reject(err)
resolve(data)
})
})
}
promisify 是 utils 工具类里自带的一个方法,可以帮助我们简化步骤,以上代码可以变为下面这样
const fs = require('fs')
const util = require('util')
let read = util.promisify(fs.readFile)
read(path.resolve(__dirname, 'name.txt'), 'utf8').then(data => {})
划重点
由此可见,promisify是一个高阶函数,第一层接收fn,第二层接收fn的参数,接着我们只需要像图1那样,内部包装一个promise既可,还是很简单的
源码解析
// es5function写法,看的清楚点
function promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn(...args, function (err, data) {
if (err) reject(err)
resolve(data)
})
})
}
}
// 熟练箭头函数的话,可以这么写,es6简化版本
const promisify = fn => (...args) =>
new Promise((resolve, reject) => {
fn(...args, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
// 使用方式
let read = promisify(fs.readFile)
read('name.txt', 'utf8').then(data => {
console.log(data)
})
all 的实现
all 的基本使用
const fs = require('fs').promises
const path = require('path')
function readFile(filename) {
return fs.readFile(path.resolve(__dirname, filename), 'utf8')
}
Promise.all([1, readFile('age.txt'), 2, readFile('name.txt'), 3, 4])
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
//输出 [ 1, '28岁', 2, 'age.txt', 3, 4 ]
使用介绍
all 是 Promise 的静态方法,同 resolve 和 reject
参数接收一个数组,数组里既可以放 promise 也可以放普通值
按照数组里的排列执行顺序,依次执行
只有所有的promise全部成功才算成功,有一个失败就失败
分析样例代码
从上面样例代码,可以看出,Promise.all 返回的是一个 promise,而内部执行数组的时机也应该是并发,所以可以用 for 循环依次执行,而要保证 promsie 执行顺序,我们需要一个计数器,当普通值和 promise 全部执行完毕后,计数器应该和传入的数组长度相等,此时 resolve 结果就可以了
源码实现
static all(promises) {
return new Promise((resolve, reject) => {
let index = 0 // 计数器
let result = []
const isPromise = (item) => {
return typeof item.then === 'function'
}
const processData = (key, item) => {
result[key] = item
if(++index === promises.length) {
resolve(result)
}
}
for(let i=0,length=promises.length;i<length;i++) {
let item = promises[i]
if(isPromise(item)) {
item.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, item)
}
}
})
}
all 的缺点
从上述代码可以看到,for 循环并不会因为当前 all 的 promise 被 reject 了而停止,all是不能被中断的,假设我们传入的数组,第 1 个 promise 失败了,实际上,后面的都可以不用再做了,因为一个失败则 Promise.all 就返回失败原因。
race 的实现
race 的基本使用
const fs = require('fs').promises
const path = require('path')
function readFile(filename) {
return fs.readFile(path.resolve(__dirname, filename), 'utf8')
}
Promise.race([1, readFile('age1.txt'), 2, readFile('name.txt')])
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
// 输出 1
// 如果当前根目录下没有age1.txt的话,返回错误,因为错误比较快
Promise.race([readFile('age1.txt'), readFile('name.txt')])
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
// 输出Error: ENOENT: no such file or directory
// 如果当前根目录下有age.txt和name.txt的话,就看谁写读到文件返回了。
Promise.race([readFile('age.txt'), readFile('name.txt')])
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
// 输出既可能是 age.txt 也可能是 28岁
使用介绍
Promise.race 是赛跑的意思,就是谁快就用谁的返回结果
如果传入的数组里有普通值,就用普通值,否则就看哪个接口先完成,就能哪个接口的返回值
如果某个接口失败了,而失败比较快的话,就返回失败结果。
源码实现
static race(promises) {
return new Promise((resolve, reject) => {
const isPromise = (item) => {
return typeof item.then === 'function'
}
for(let i=0,length=promises.length;i<length;i++) {
let item = promises[i]
if(isPromise(item)) {
item.then(resolve,reject)
} else {
resolve(item)
}
}
})
}
promise 中断
Promise.race 的一种用法
Promise.race 看似很鸡肋,但我们可以利用它的特性,实现 Promise 的中断
注意,race 和 all 一样,并不是说传入的 promise 返回 reject 了就不走了,for 循环还是会全部执行完的,这里说的中断其实是一种假中断
我们把 promise 中断了,其实只是不要 promise 的返回值了
中断代码实现
let p = new Promise((resolve, reject) => {
// 构造一个promise,模拟ajax,10妙后返回结果
setTimeout(() => {
resolve('成功')
}, 10000)
})
// 实现一个包装函数
const wrap = promise => {
let abort
// 内部构建一个promise,将reject赋予临时变量abort
let myP = new Promise((resolve, reject) => {
abort = reject
})
const p = Promise.race([promise, myP])
// 将自己的promise的reject赋给p,用于外部手动中断promise
p.abort = abort
return p
}
// 如果promsie返回超过2妙,就中断它,不要返回结果了
let newP = wrap(p)
newP.then().catch(err => {
console.log(err)
})
setTimeout(() => {
newP.abort('ajax 超过2秒,超时了')
}, 2000)
代码解析
写一个包装函数 wrap,内部构造一个 promise,将传入 wrap 的 promise 和自己构造的 promise,用 Promise.race 组合一下,将自己构造的 promise 的 reject 方法赋给组合后的 promise 并返回,此时只需手动调用构造的 promise 的 reject 方法,就可以使 Promise.race 返回错误,实现中断的功能