特皮技术团队:一年前端手写Promise

从零手写Promise

  • 在这个前端快速发展的时代,今天不努力,明天变垃圾,虽然这是一个表情包上看到的,相信大家一定都看过,说实在的,这话是粗,但是事实就是如此。
  • 言归正传我们看看今天的内容把。
  • 上次我们看了js的重点也是难点 “异步”,趁热打铁,今天看看从零手写Promise

我们先看看代码
首先我们先来回顾一下Promise的用法

new Promise((resolve,reject)=>{

resolve('正确的')

}).then(value =>{

console.log(value);

})

  • 可以看的到在浏览器打印出了 "正确的"Promise的用法并不难,我们只要知道 new Promise Promise传入function(resolve,reject),接着在函数内做需要做的事情即可,等到需要的时候,对这个构造函数.then(res=>{})即可获取对应的值

下面我们就来分析一下Promise应该如何实现

  • 首先我们应该定义一个Promise的结构
  • 本文以为es5 的方式

(function (window) {

function MyPromise(executor) { // 1 定义MyPromise 构造函数

function resolve(value) { // 定义resolve

}

function reject(reason) { // 定义reject

}

MyPromise.prototype.then = function (onResolved,onRejected) { // 定义then

}

MyPromise.prototype.catch = function (error) { // 定义catch

}

// 定义执行器的执行

executor(resolve,reject);

}

window.MyPromise = MyPromise; // 2导出

})(window)

  • 接着可以看看下面的执行

(function (window) {

// Promise

function Mypromise(executor) {

const self = this; // 需要定义一下this指向

self.status = 'pennding'; // 初始化状态

self.data = undefined; // promise对象指定一个用于存储结果数据

self.callbacks = []; // 每个元素的结构 { onResolved(){}, onRejected(){}}

function resolve(value) { // resolve

if (self.status !== 'pennding') { // 因为promise 状态只能修改一次

return;

}

self.status = 'resolve'; // 改变为 resolve

self.data = value; // 保存value的值

if (self.callbacks.length > 0) { // 如果有待执行的callback函数,立即执行回调函数onResolved

setTimeout(() => { // 当前方案是为了将任务挂在队列中,制造异步

self.callbacks.forEach(callbacksObj => {

callbacksObj.onResolved(value)

})

},0)

}

}

function reject(reason) { //reject

if (self.status !== 'pennding') { // 因为promise 状态只能修改一次

return;

}

self.status = 'reject'; // 改变为 reject

self.data = reason; // 保存reason的值

if (self.callbacks.length > 0) { // 如果有待执行的callback函数,立即执行回调函数onRejected

setTimeout(() => {

self.callbacks.forEach(callbacksObj => {

callbacksObj.onRejected(reason)

})

},0)

}

}

try { // 如果执行器抛出异常

executor(resolve, reject);

} catch (error) {

reject(error)

}

}

// Promise.then()

Mypromise.prototype.then = function (onResolved, onRejected) {

// 假设当前状态还是pennding

const self = this;

self.callbacks.push({

onResolved,

onRejected

})

}

// Promise.catch()

Mypromise.prototype.carch = function (error) {

}

window.Mypromise = Mypromise;

})(window);

<body>

<script></script>

<script>

const p = new Mypromise((resolve, reject) => {

setTimeout(() => { // 因为拆分 then还未处理,需要p.then 先执行

resolve(1)

console.log("我先")

}, 100)

})

p.then(value => {

console.log("onResolve()1", value)

}, reason => {

console.l("onReject()1", reason)

})

p.then(value => {

console.log("onResolve()2", value)

}, reason => {

console.l("onReject()2", reason)

})

</script>

</body>

  • 可以看的到执行结构是异步的,第一步成功了。

上面的代码我们来讲解一下,

  • 1、首先是通过es5写的,所以需要导出我们使用了 闭包

  • 2、 创建Promise构造函数 并导出

  • 3、我们使用promise时都知道往里面传入一个函数,这个函数带有resolve,reject并且在函数里面执行需要执行的操作,

所以这个接受的参数叫做执行器(executor)

  • 4、执行器里执行的是两个函数

  • 5、我们都知道promise有三个状态其中初始化时pendding

  • 6、根据resolve,reject改变状态,改变值

  • 7、我们都知道promise构造函数还有then方法和catch方法

  • 8、别忘了promise也可以throw Error,所以执行器(executor)需要修改一下

  • 9、最后调用并执行看看结果

<body>

<script></script>

<script>

const p = new MyPromise((resolve, reject) => {

setTimeout(() => { // 因为拆分原因then 还未做对应处理,此时不能改变状态

resolve(1)

console.log("我先")

}, 100)

})

p.then(value => {

console.log("onResolve()1", value)

}, reason => {

console.l("onReject()1", reason)

})

p.then(value => {

console.log("onResolve()2", value)

}, reason => {

console.l("onReject()2", reason)

})

</script>

</body>

  • 怎么结果不对呢
  • 再来看看 因为直接执行的回调是同步执行的,需要将任务放入队列中

  • 此时结果正确了。
  • 最后别忘了promise状态只允许修改一次

  • 好的上面是简易版的Promise并不包括then的实现。

接下来我们在上面的基础对then以及整体进行了一个升级

  • 代码如下

(function (window) {

const PENDDING = 'pendding';

const FULFILLED = 'fulfilled';

const REJECTED = 'rejected';

function MyPromise(executor) { // 定义MyPromises构造函数

const self = this;

self.status = PENDDING;

self.data = undefined;

self.callbacks = [];

function resolve(value) {

if (self.status === PENDDING) {

self.status = FULFILLED; // 改变MyPromise状态

self.data = value; // MyPromise 的值对应变化

setTimeout(() => { // 异步执行

self.callbacks.forEach(callbacksObj => { // 如果有待执行的callback函数,立即执行回调

callbacksObj.onResolved(value)

})

})

}

}

function reject(reason) {

if (self.status === PENDDING) {

self.status = REJECTED;

self.data = reason;

setTimeout(() => {

self.callbacks.forEach(callbacksObj => {

callbacksObj.onRejected(reason);

});

})

}

}

try { // MyPromise可以抛出异常

executor(resolve, reject);

} catch (error) {

reject(error)

}

}

/*

MyPromise原型对象的then()

指定成功和失败回调

返回一个新的回调函数

// 返回的MyPromise结果由onResolved/onRejected的结果决定

*/

MyPromise.prototype.then = function (onResolved, onRejected) { // 定义then

const self = this;

// 指定回调函数的默认值(必须是函数)

onResolved = typeof onResolved==='function' ? onResolved : value => value;

onRejected = typeof onRejected==='function' ? onRejected : reason => {throw reason};

return new MyPromise((resolve,reject)=>{ // 返回一个新的MyPromise对象

function handle(callback) {

// 返回的MyPromise结果由onResolved/onRejected的结果决定

// 1、抛出异常MyPromise结果为失败 reason为结果

// 2、返回的是MyPromise MyPromise为当前的结果

// 3、返回的不是MyPromise value为结果

// 需要通过捕获获取才能知道有没有异常

try{

const result = callback(self.data)

// 判断是不是MyPromise

if ( result instanceof MyPromise){

// 只有then才知道结果

result.then(value=>resolve(value),reason=>reject(reason))

}else{

resolve(result)

}

}catch(error){

reject(error) // 返回的结果为reject(error) 上面第一点

}

}

// 判断当前的status

if (self.status === FULFILLED){ // 状态为 fulfilled

setTimeout(()=>{ // 立即执行异步回调

handle(onResolved);

})

} else if (self.status === REJECTED){ // 状态为 rejected

setTimeout(()=>{ // 立即执行异步回调

handle(onRejected);

})

}else{ // pendding将成功和失败保存到callbacks里缓存起来

self.callbacks.push({

onResolved(value){ //函数里面调用回调函数 并且根据回调函数的结果改变MyPromise的结果

handle(onResolved) //为什么这里没有setTimeout,因为上面已经写到过改变状态后回去callbacks里循环待执行的回调函数

},

onRejected(reason){

handle(onRejected)

}

})

}

})

}

MyPromise.prototype.catch = function (onRejected) { //定义then

return this.then(undefined,onRejected)

}

window.MyPromise = MyPromise; // 导出MyPromise

})(window)

  • 相信大家看到这个代码跟我是一样的反应,我去,怎么跟之前不一样了
  • 实际上当我动手去实现了手写Promise以后我觉得真的并不难,如果要说难这个then的实现,稍微比上面难一些。
  • 好了接下来我讲解一下
  • 1、首先是在原来的基础上去定义了一些常量方便使用

  • 2、要知道Promise.then返回的是什么

  • 3、接着判断一下状态

  • 4、好的接下来我们对相应的状态进行操作

  • 5、可以看到pendding状态是没有添加定时器的,为什么不是异步呢,因为是往callbacks存储,然后上面执行到对应位置会进行循环判断,且在循环操作时是异步的,,完善pendding

  • 6、前面的代码可以看得出有四个位置代码相似度很高,所以定义一个函数,进行封装(注意要在new Promise 里)

  • 7、再设置一下默认值

  • 8、再来完善一下catch

接下来我们进行测试

  • 看来没为什么问题
  • 再来试试catch


  • 新手第一次写的还是不怎么样,但是每一步都是通过自己是如何学,实实在在的敲出来的,希望能够帮助到和我一样想学好promise的朋友。从开始的很难到后面能直接写出来,其实花费的时间还是不少的,但是收货颇丰啊。

以上是 特皮技术团队:一年前端手写Promise 的全部内容, 来源链接: utcz.com/a/39280.html

回到顶部