如何实现一个 Promise ?

154 阅读1分钟

Promise 是什么

Promise 是一种异步编程解决方案。(还有 Generator 也是)

为什么要用 Promise

异步逻辑需要写在回调函数中,使用 promise 可以以链式调用的方式处理异步逻辑

原理

收集回调函数,按顺序调用

使用方式

const mockAjax = (url, s, callback) => {
    setTimeout(() => {
        callback(url + '异步请求耗时' + s + '秒');
    }, 1000 * s)
}
const pUserId = new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
})
const pUserName = new Promise(resolve => {
    mockAjax('getUserName', 2, function (result) {
        resolve(result);
    })
})

pUserId.then(id => {
    console.log(id)
    return pUserName
}).then(name => {
    console.log(name)
}).catch(error => {
    console.log('b', error);
}).finally({ 'a': 5 })

简单实现

class Promise {
	state = 'pendding' // 这里相当于在 constructor 中写 this.state = 'pendding'
    value = null
    callbacks = []
	constructor(fn) {
    	fn(this._resolve.bind(this), this._reject.bind(this))
    }
    then(onFulfilled, onRejected) {
    	return new Promise((resolve, reject) => {
        	this._handle({onFulfilled, onRejected, resolve, reject})
        })
    }
    _resolve(value) {
    	value instanceOf Promise
            ? value.then.call(value, this._resolve.bind(this), this._reject.bind(this))
            : this._changeState('fulfilled', value)
    }
    _reject(value) {
    	this._changeState('rejected', value)
    }
    _changeState(state, value) {
    	this.state = state
        this.value = value
        this.callbacks.forEach(callback => this._handle(callback))
    }
    _handle(callback) {
    	const dict = {
        	'pendding': () => this.callbacks.push(callback),
            'fulfilled': callback.onFulfilled
                            ? callback.resolve(callback.onFulfilled(this.value))
                            : callback.resolve(this.value),
            'rejected': callback.onRejected
            			 	? callback.reject(callback.onRejected(this.value))
            				: callback.reject(this.value),
        }
        dict[this.state]()
    }
}

补充静态方法

class Promise {
	state = 'pendding'
    value = null
    callbacks = []
	constructor(fn) {
    	fn(this._resolve.bind(this), this._reject.bind(this))
    }
    then(onFulfilled, onRejected) {
    	return new Promise((resolve, reject) => {
        	this._handle({onFulfilled, onRejected, resolve, reject})
        })
    }
    catch(onError) {
        return this.then(null, onError);
    }
    finally(onDone) {
        if (typeof onDone !== 'function') { return this.then() }
        return this.then(onDone, onDone)
    }
    _resolve(value) {
    	value instanceOf Promise
            ? value.then.call(value, this._resolve.bind(this), this._reject.bind(this))
            : this._changeState('fulfilled', value)
    }
    _reject(value) {
    	this._changeState('rejected', value)
    }
    _changeState(state, value) {
    	this.state = state
        this.value = value
        this.callbacks.forEach(callback => this._handle(callback))
    }
    _handle(callback) {
    	const dict = {
        	'pendding': () => this.callbacks.push(callback),
            'fulfilled': callback.onFulfilled
                            ? callback.resolve(callback.onFulfilled(this.value))
                            : callback.resolve(this.value),
            'rejected': callback.onRejected
            			 	? callback.reject(callback.onRejected(this.value))
            				: callback.reject(this.value),
        }
        dict[this.state]()
    }
    static resolve(value) {
    	if(!value) { return new Promise((resolve) => { resolve() }) }
        if(value instanceOf Promise) { return value }
        if(typeof value === 'object' && typeof value.then === 'function') { 
        	return new Promise((resolve) => { value.then(resolve) })
        }
        return new Promise((resolve) => { resolve(value) }) 
    }
    static reject(value) {
    	if(typeof value === 'object' && typeof value.then === 'function') { 
        	return new Promise((resolve, reject) => { value.then(reject) })
        }
        return new Promise((resolve, reject) => { reject(value) }) 
    }
    static all(promises) {
    	return new Promise((resolve, reject) => {
        	promises.reduce((acc, cur, idx, src) => {
            	Promise.resolve(cur).then((result)=>{
                	acc[idx] = result
                    src.length === acc.length && resolve(acc)
                },(err) => { reject(err) })
            	return acc
            }, [])	
        })
    }
    static race(promises) {
    	return new Promise((resolve, reject) => {
        	promises.forEach((promise) => {
            	Promise.resolve(promise).then(
                	(result) => { resolve(result) },
                    (err) => { reject(err) },
                )
            })
        })
    }
}