李志成的个人网站

李志成的个人网站

  • 博客
  • ·
  • 留言
  • ·
  • 友链
  • ·
  • 关于
  • ·
  • Rss

原生 js 模拟实现 es6 中的 Promise

发表于2019-02-28 14:23:55分类于javascript0条评论阅读次数134

概述

在开篇之前,我们先来看一个promise的基础实列,看看promise最基本的用法。

promise 基础实例

var p = new Promise(function (resolve, reject) {
    console.log('begin to execute!');
    const num = 1;
    if (num === 0) {
        resolve('Success!');
    } else {
        reject('Failure');
    }
});
p.then(function (data) {
    console.log('resolve: ', data);
}).catch(err => {
    console.log('reject', err)
});

在实列当中,我们关注一个创建一个 promise 需要 new ,其次就是可以进行链式操作,该操作包括 then,catch。 那么为了简单起见,我们先关注这几个核心点。

1、几个关键词: promise, then, catch是可以进行链式操作的。

2、在网上的一些资料我们可以了解到,一个promise拥有一个状态status:pending,resolved, rejected

3、es6的 promise 传入函数后是立刻运行的。

那么接下来就实现一个简单的模型来实现这个关键点:

代码如下:

const status = {
   PENDING: 'pending',
   RESOLVED: 'resolved',
   REJECTED: 'rejected'
}
class PromiseNew {
    constructor(resolver) {
        this.status = status.PENDING;
        this.value = null;
        this.resolvedQueue = []
        this.rejectedQueue = []
        resolver(this.resolve.bind(this), this.reject.bind(this));
    }
    addQueue(self, resolve, reject) {
        typeof resolve === 'function' && self.resolvedQueue.push(resolve);
        typeof reject === 'function' && self.rejectedQueue.push(reject);
    }
    // 同时注册成功和失败处理函数
    then(success, fail) {
        if (this.status === status.PENDING) {
            this.addQueue(this, success, fail);
        }
        else if (typeof success === 'function' && this.status === status.RESOLVED) {
            success(this.value);
        } else if (typeof fail === 'function' && this.status === status.REJECTED) {
            fail(this.value);
        }
        return this;
    }
    resolve(value) {
        this.status = status.RESOLVED;
        this.value = value;
        this.resolvedQueue.forEach((item) => {
            this.value = item(this.value);
        })
    }
    // Promise规范当中,规定Promise只能从初始pending状态变到resolved或者rejected状态,是单向变化的
    reject(value) {
        this.status = status.REJECTED;
        this.value = value;
        this.rejectedQueue.forEach((item) => {
            this.value = item(this.value);
        })
    }
    catch(fn) {
        if (typeof fn === 'function') {
            if (this.status === status.PENDING) {
                this.addQueue(this, null, fn);
            }
            else if (this.status === status.REJECTED) {
                fn(this.value);
            }
        }
        return this;
    }
}

代码原理

1、当执行new PromiseNew(funciton)时,该function传入到resolver进行保存,并绑定this。

2、为了可以进行链式操作,then,catch函数都应该直接返回this。

3、为了可以让链式操作正常执行,我们把 then 中的函数放到成功队列resolvedQueue; 把 catch 中的函数放到rejectedQueue队列。并通过this.value接受每个函数返回的值。

4、为什么用resolvedQueue和rejectedQueue队列解决链式问题? 观察promise的写法,如promise.then().then().then()...实际就是从上到下,每一个函数逐步执行,只需要把每个函数收集起来。

下面将会实现 Pormise 最常用的几个api。

1. Promise.prototype.resolve
2. Promise.prototype.reject
3. Promise.prototype.then
4. Promise.all
5. Promise.resolve
6. Promise.reject

代码如下

/**
    * Promise类实现原理
    * 构造函数传入一个function,有两个参数,resolve:成功回调; reject:失败回调
    * status: 状态存储 [PENDING-进行中 RESOLVED-成功 REJECTED-失败]
    * resolvedQueue: 成功处理函数列表
    * rejectedQueue: 失败处理函数列表
    * then: 同时注册成功和失败处理函数
    * resolve: 更新state为:RESOLVED,并且执行成功处理队列
    * reject: 更新state为:REJECTED,并且执行失败处理队列
**/
const status = {
    PENDING: 'pending',
    RESOLVED: 'resolved',
    REJECTED: 'rejected'
}

class PromiseNew {
    constructor(resolver) {
        this.status = status.PENDING;
        this.value = null;
        this.resolvedQueue = []
        this.rejectedQueue = []
        resolver(this.resolve.bind(this), this.reject.bind(this));
    }
    addQueue(self, resolve, reject) {
        typeof resolve === 'function' && self.resolvedQueue.push(resolve);
        typeof reject === 'function' && self.rejectedQueue.push(reject);
    }
    // 同时注册成功和失败处理函数
    then(success, fail) {
        if (this.status === status.PENDING) {
            this.addQueue(this, success, fail);
        }
        else if (typeof success === 'function' && this.status === status.RESOLVED) {
            success(this.value);
        } else if (typeof fail === 'function' && this.status === status.REJECTED) {
            fail(this.value);
        }
        return this;
    }
    resolve(value) {
        this.status = status.RESOLVED;
        this.value = value;
        console.log(this)
        this.resolvedQueue.forEach((item) => {
            this.value = item(this.value);
        })
    }
    // Promise规范当中,规定Promise只能从初始pending状态变到resolved或者rejected状态,是单向变化的
    reject(value) {
        this.status = status.REJECTED;
        this.value = value;
        this.rejectedQueue.forEach((item) => {
            this.value = item(this.value);
        })
    }
    catch(fn) {
        if (typeof fn === 'function') {
            if (this.status === status.PENDING) {
                this.addQueue(this, null, fn);
            }
            else if (this.status === status.REJECTED) {
                fn(this.value);
            }
        }
        return this;
    }
}

PromiseNew.resolve = (value) => {
    return new PromiseNew((resolve, reject) => {
        resolve(value);
    })
}

PromiseNew.reject = (value) => {
    return new PromiseNew((resolve, reject) => {
        reject(value);
    })
}

PromiseNew.all = promiseArr => {
    return new PromiseNew(function (resolve, reject) {
        let proNum = promiseArr.length;
        let count = 0;
        let messageMap = [];
        // 统一根据状态进行判断
        function handle(promise) {
            // 判断是否已经改变
            if (promise.status == status.RESOLVED) {
                messageMap.push(promise.value); // 不论先后,先存入map中
                count++;
                // 所有都resolved才能够,达到总体的resolved
                if (count == proNum) {
                    resolve(messageMap)
                }
            } else {
                // 只要出现一个reject,则全部都rejected掉
                reject(new Error(promise));
            }
        }
        promiseArr.forEach((promise, index) => {
            if (!(promise instanceof PromiseNew)) throw Error('必须传入PromiseNew对象');
            return promise.then(res => {
                handle(promise, index)
            }).catch(err => {
                handle(promise, index)
            });
        });
    })
}

测试代码

PromiseNew.resolve('测试异常').then(res => {
    console.log(res)
}).catch((res) => {
    console.log(res);
})
PromiseNew.all([
    new PromiseNew((resolve, reject) => {
        setTimeout(() => {
            resolve('异步函数');
        })
    }),
    new PromiseNew((resolve, reject) => {
        setTimeout(() => {
            resolve('异步函数1');
        })
    }),
    new PromiseNew((resolve, reject) => {
        setTimeout(() => {
            resolve('异步函数2');
        })
    })],
).then(res => {
    console.log(res, 1000)
});
new PromiseNew((resolve, reject) => {
    setTimeout(() => {
        resolve('hello world');
    })
}).then((res) => {
    console.log(res, 1200);
}).catch((res) => {
    console.log(res, 'failure');
})

总结

在开始实现 promise 的时候,我也是很难理解 promise 的then, catch的运行机制。当时只要耐心一点重复观看几次promise的实际逻辑操作,你会发现要实现这几个函数也不是很难。其实在这个过程中,只要理解好,promise 的状态时单向转变的,主要执行的函数都在resolve和rejected中,因为它们是需要一个延时操作的。才能进行回调数据。而状态则是在一开始判断resolve或者rejected是否被执行了,如果被立刻执行了,那么就要在then和catch中进行补充执行(即根据相应的状态进行再次回调resolve和rejected)

  • 状态status: pending -> resolved ; oending -> rejected

  • pending -> 若resolve处在异步中,则收集 then 函数进队列, 否则在 then 函数中 再次调用 resolve 进行回调 - -> resolved 完成一个 promise 过程

  • rejected 同理

--发表评论--

🚀support markdown (* ̄▽ ̄*)ブ