前言
相信来看手写Promise的小可爱们,都或多或少知道Promise的使用方法和作用。所以这里就不详细介绍Promise的历史背景和作用,简单介绍一下,各种使用方法,和自己手写Promise的过程,作为一个学习记录。
使用方法
写一个Promise的测试文件。版本比较高的node版本已经支持Promise写法,直接node运行此文件来进行调试即可。
// Promise.js
const fs = require("fs")
function asyncReadFile() {
return new Promise((resolve, reject) => {
fs.readFile('./demo.txt', (err, data) => {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
}
function eventError() {
return new Promise((resolve, reject) => {
fs.readFile('./demo.txt', (err, data) => {
reject("错误内容")
})
})
}
function _setTimeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("setTimeout")
}, 1500)
})
}
function sync() {
return new Promise((resolve, reject) => {
resolve(88)
})
}
// then接受成功方法
asyncReadFile()
.then(ret => console.log(ret.toString())) // 输出 dome.txt的内容
// then接受错误方法
eventError()
.then(null, err => console.log(err)) // 错误内容
// catch方法
eventError()
.catch(err => console.log(err)) // 错误内容
// resolve同步
sync()
.then(err => console.log(err)) // 错误内容
// then的链式调用,接受上个 then 的 return 值,并且是一个新的promise对象
_setTimeout
.then(ret => console.log(ret)) // 1.5后打印 setTimeout
.then(ret => { console.log(ret); return 'aaa' }) // undefined
.then(ret => console.log(ret)) // aaa
// all静态方法。接受一个Promise实例数组。等所有实例异步完成以后执行then, 返回结果对应。
Promise
.all([asyncReadFile(), _setTimeout()])
.then(ret => {
console.log(ret) // ['demo.txt文件内容', 'setTimeout']
})
// race静态方法。接受一个Promise实例数组。其中实例异步完成以后执行then, 返回它的结果。
Promise
.race([asyncReadFile(), _setTimeout()])
.then(ret => {
console.log(ret)
})
Promise.resolve(3).then(ret => console.log(ret))
// then 的链式调用并且return 一个Promise 对象,等改Promise执行完一会在执行then, then参数为改Promise的resolve/reject值。这个不在这次手写中验证
// 打印顺序 'demo.txt文件内容' ->(1.5s later...) 'setTimeout'
asyncReadFile().then(ret => {
console.log(ret.toString())
return _setTimeout()
})
.then(ret => {
console.log(ret)
})
// 漏洞
new Promise((resolve) => {
setTimeout(() => resolve(1), 500)
setTimeout(() => resolve(2), 1500)
})
.then(ret => console.log(ret))
总结下Promise的一些基本结构:
- promise是一个类。
- 构造函数的参数是一个函数fn,fn 的参数有2个参数,一个参数是成功时候的调用,一个是失败时候调用
- promise对象有一个then方法,一个是成功的回调,一个是失败的回调
- then方法支持链式调用
- then返回了一个新的promise对象
- promise对象有一个 catch方法,捕捉reject的抛出的错误
- Promise静态方法all, race;
- Promise 状态只能从pending到 reslove 或者reject 的状态
Promise是一个类
class Promise {
}
构造函数第一个参数为一个函数,该函数暴露的2个函数作为参数,第一个为参数为成功的时候调用,第二个为失败的时候调用
class Promise {
constructor(fn) {
const resolve = () => {
console.log('sucess!')
}
const reject = () => {
console.log('reject!!!')
}
fn(resolve, reject)
}
}
实现基本结构和then异步调用 -- 发布订阅
结合上面特点,我们实现一个简单的Promise
class _Promise {
constructor(executor) {
this.status = 'pending' // resolved rejected
this.value = null
this.reason = null
this.onResolveCallbacks = [] // 成功的容器
this.onRejectedCallbacks = [] // 失败的容器
const resolve = function(value) { //成功调用的resolve函数
this.status = 'resolved'
this.value = value
if(this.onResolveCallbacks.length === 0) return
this.onResolveCallbacks.forEach(fn => fn(this.value))
}
const reject = function(reject) { // 失败调用的reject函数
console.log(this)
this.status = 'rejected'
this.reason = reject
if(this.onRejectedCallbacks.length === 0) {
console.warn('UnhandledPromiseRejectionWarning:', this.reason)
console.warn('UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().')
return
}
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
executor(resolve.bind(this), reject.bind(this))
}
then(onFulfilled, onRejected) { // 内部 then 实现,把处理函数推入容器
if(this.status === 'pending') {
onFulfilled && this.onResolveCallbacks.push(onFulfilled)
onRejected && this.onRejectedCallbacks.push(onRejected)
}
if(this.status === 'resolved') {
onFulfilled && onFulfilled(this.value)
}
if(this.status === 'rejected') {
onRejected && onRejected(this.reason)
}
}
catch(onRejected) { // 原理同 then
if(this.status === 'pending') {
this.onRejectedCallbacks.push(onRejected)
}
if(this.status === 'rejected' || this.status === 'resolved') {
onRejected(this.reason)
}
}
}
const fs = require("fs")
function asyncReadFile() {
return new _Promise((resolve, reject) => {
fs.readFile('./demo.txt', (err, data) => {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
}
asyncReadFile()
.then((res) => {
console.log(res, '----')
})
上面的实现了基本的方法,但是还存在许多问题,比如还没有链式调用等等... executor(resolve.bind(this), reject.bind(this)) 这里需要绑定this对象,不然 reslove/reject 暴露出去以后拿不到this对象
实现then的同步调用 -- 之漏洞一
上面的实现有个问题,就是 当reslove/reject 不是在异步的函数里面的时候,then的2个函数参数将不会放入到 订阅发布的容器中。进行分析可以知道:当调用 new _Promise的时候,由于reslove同步执行,导致先进行了发布,还没来得及进行订阅; 我的解决方案是,加一个setTimeout
constructor(executor) {
const resolve = function(value) {
setTimeout(() => {
this.status = 'resolved'
this.value = value
if(this.onResolveCallbacks.length === 0) return
this.onResolveCallbacks.forEach(fn => fn(this.value))
}, 0)
}
const reject = function(reject) {
setTimeout(() => {
this.status = 'rejected'
this.reason = reject
if(this.onRejectedCallbacks.length === 0) {
console.warn('UnhandledPromiseRejectionWarning:', this.reason)
console.warn('UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().')
return
}
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}, 0)
}
executor(resolve.bind(this), reject.bind(this))
}
经过测试,解决了同步拿不到值得情况。
实现then的链式调用
上面的还没有实现链式调用,测试时候会发现会报错。
then(onFulfilled, onRejected) {
return new _Promise((resolve, reject) => {
if(this.status === 'onFulfilled') {
this.resolveArr.push(() => {
const x = onFulfilled(this.value)
resolve(x)
})
}
if(this.status === 'onRejected') {
this.rejectArr.push(() => {
const x = onRejected(this.value)
reject(x)
})
}
if(this.status === 'pending') {
this.resolveArr.push(() => {
const x = onFulfilled(this.value)
resolve(x)
})
this.rejectArr.push(() => {
const x = onRejected(this.value)
reject(x)
})
}
})
}
实现catch方法
catch(onRejected) {
return this.then(null, onRejected)
}
实现静态方法resolve
_Promise.resolve = value => new _Promise(resolve => resolve(value))
实现静态方法reject
_Promise.reject = value => new _Promise((resolve, reject) => reject(value))
实现静态方法 all
_Promise.all = function(promises) {
// if(Object.prototype.toString.call(promises) !== '[object Array]') return []
let res = []
return new this((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let promiseItem = promises[i];
if(typeof promiseItem !== 'object') promiseItem = this.resolve(promiseItem)
promiseItem.then(ret => res.push(ret))
}
resolve(res)
})
}
实现静态方法 race
_Promise.race = promises =>
new _Promise((resolve, reject) =>
promises.forEach(pro => pro.then(resolve, reject))
)