Promise 源码:实现一个简单的 Promise

前言

Promise 是 ES6 新增的一个内置对象, 它是用来避免回调地狱的一种解决方案。

从以前一直嵌套传回调函数,到使用 Promise 来链式异步回调。Promise 究竟是怎么实现,从而达到回调函数“扁平化”?

接下来就来一步步实现一个简单的 Promise。开始发车了...

执行步骤

先来看一个使用 Promise 的简单例子:

var p = new Promise(function a (resolve) {
  console.log(1);
  setTimeout(function () {
    resolve(2);
  }, 1000);
})

p.then(function b (val) {
  console.log(val);
});
复制代码

代码执行,它会先执行函数 a,打印出 1。定时器 1 秒后执行 resolve,紧接着执行了函数 b。

更详细的步骤是这样子的:

  1. new Promise,执行 Promise 构造函数;
  2. 构造函数里,执行 a 函数;
  3. 执行 then 函数;
  4. 1 秒后,执行 resolve 函数;
  5. 执行 b 函数。

这里的一个思路就是,在 then 函数执行时用一个属性保存函数 b,然后在 resolve 执行时再将其执行。

开始封装

这里定义一个 MyPromise,它有 then 函数,还有一个 callback 属性用来保存上面的“b 函数”。

function MyPromise (fn) {
  var _this = this;
  
  // 用来保存 then 传入的回调函数
  this.callback = undefined;
  
  function resolve (val) {
    _this.callback && _this.callback(val);
  }
  
  fn(resolve);
}

MyPromise.prototype.then = function (cb) {
  this.callback = cb;
};
复制代码

测试使用:

var p = new MyPromise(function (resolve) {
  console.log(1);
  setTimeout(function () {
    resolve(2);
  }, 1000);
});

p.then(function (val) {
  console.log(val);
});
复制代码

代码执行时会马上打印出 1,1秒后打印出 2。毛问题。

多个 resolve 调用处理

上面已经实现了一个简单的 Promise,当然还有很多种情况需要考虑。

比如会有这么一种情况,调用了多个 resolve 函数:

var p = new MyPromise(function (resolve) {
  console.log(1);
  setTimeout(function () {
    resolve(2);
    resolve(3);
    resolve(4);
  }, 1000);
});
复制代码

原生的 Promise 在调用了第一个 resolve 之后,后面的 resolve 都无效化,即后面的 resolve 都是没用的代码。

这里的处理方式是,给 MyPromise 再添加一个属性 isResolved,用来记录是否调用过 resolve 函数。如果调用过,用它标识一下。再有 resolve 的调用,则用它判断返回。

function MyPromise (fn) {
  var _this = this;

  this.callback = undefined;
  this.isResolved = false;
  
  function resolve (val) {
    if (_this.isResolved) return;
    _this.isResolved = true;
    _this.callback && _this.callback(val);
  }
  
  fn(resolve);
}
复制代码

多个 then 处理

继续走,除了可以调用多个 resolve 函数,同样我们可以调用多个 then 函数。

var p = new MyPromise(function (resolve) {
  console.log(1);
  setTimeout(function () {
    resolve(2);
  }, 1000);
});

p.then(function (val) {
  console.log(val);
});

p.then(function (val) {
 console.log(val);
});
复制代码

与 resolve 不同,这里的每一个传给 then 的回调函数都会在 1 秒后执行,即 then 函数都有效。代码执行,先打印出 1。1 秒后,打印两个 2。

所以 MyPromise 的 callback 属性需改成数组的格式,保存着每一个 then 的回调函数。

function MyPromise (fn) {
  var _this = this;

  this.callback = [];
  this.isResolved = false;
  
  function resolve (val) {
    if (_this.isResolved) return;
    _this.isResolved = true;
    
    if (_this.callback.length > 0) {
      _this.callback.forEach(function (func) {
        func && func(val);
      });
    }
  }
  
  fn(resolve);
}

MyPromise.prototype.then = function (cb) {
  this.callback.push(cb);
};
复制代码

在多次调用 then 时,MyPromise 通过属性 callback 来保存多个回调函数。在 resolve 执行后,再去遍历 callback,将它保存的回调函数逐个执行。

支持 then 链式调用

Promise 相对于回调地狱而言,它的优势在于可以进行 then 的链式调用,从而将回调函数“扁平化”。

比如我有一个异步操作需要在另一个异步操作后才能执行,只需要继续调用 then 就能够下一步回调。

var p = new MyPromise(function (resolve) {
  console.log(1);
  setTimeout(function () {
    resolve(2);
  }, 1000);
});

p.then(function (val) {
  console.log(val);
}).then(function (val) {
  console.log(val);
});
复制代码

既然要能够 then 链式调用,那我在执行完 then 函数后返回 this 不就可以啦。但这不就跟刚刚的代码一样了吗?

p.then(function (val) {
  console.log(val);
});

p.then(function (val) {
  console.log(val);
});
复制代码

这样就会在调用 resolve 函数之后同时执行,而不是执行完第一个 then 后,再执行第二个。所以直接返回 this 的方案是不行的。

我们再想,还有什么可以返回的,并且带有 then 函数的。答案就是 new 一个新的 MyPromise 并返回。我们需重写一个 then 函数的实现。

MyPromise.prototype.then = function (cb) {
  var _this = this;

  return new MyPromise(function (resolve) {
    _this.callback.push({
      cb: cb,
      resolve: resolve
    });
  });
};
复制代码

这里 callback 重新改写了一下,保存的是 then 的回调函数,和新 new 的 MyPromise 的 resolve 函数。保存的 resolve 函数先不执行,因为我们知道,它一旦执行了,就会触发传入 then 的回调函数的执行。

同时,MyPromise 构造函数里的 resolve 也需要调整一下:

function MyPromise (fn) {
  var _this = this;

  this.callback = [];
  this.isResolved = false;
  
  function resolve (val) {
    if (_this.isResolved) return;
    _this.isResolved = true;

    if (_this.callback.length > 0) {
       _this.callback.forEach(function (item) {
       var res;
       var cb = item.cb;
       var resolve = item.resolve;
       
       cb && (res = cb(val));
       resolve && resolve(res);
     });
    }
  }
  
  fn(resolve);
}
复制代码

在执行第一个 then 的回调函数时,将其执行完返回的值,作为保存的 resolve 的参数传入。

p.then(function (val) {
  console.log(val);
  return val + 1;
}).then(function(val) {
  console.log(val);
});
复制代码

这样子,就能够链式的 then 调用。先别急,我们实现的只是 then 的同步链式调用,而我们最终要的是异步的链式调用。

我们需要这样子的:

p.then(function (val) {
  console.log(val);
  
  return new MyPromise(function (resolve) {
    setTimeout(function () {
      resolve(val + 1);
    }, 1000);  
  });
}).then(function(val) {
  console.log(val);
});
复制代码

先打印出 1,1秒后打印出第一个 then 里的 2,再过多一秒,打印出第二个 then 的 3。

所以,我们需要在取出原来保存的 cb 返回的值进行判断。如果该值是一个 MyPromise 对象,则调用它的 then,否则跟原来一样调用。

function MyPromise (fn) {
  var _this = this;

  this.callback = [];
  this.isResolved = false;
  
  function resolve (val) {
    if (_this.isResolved) return;
    _this.isResolved = true;

    if (_this.callback.length > 0) {
      _this.callback.forEach(function (item) {
        var res;
        var cb = item.cb;
        var resolve = item.resolve;
        
        cb && (res = cb(val));
        if (typeof res === 'object' && res.then) {
          res.then(resolve);
        } else {
          resolve && resolve(res);
        }
      });
    }
  }
  
  fn(resolve);
}
复制代码

最后

在我们实现的 MyPromise 里,有两个属性,分别是 isResolvedcallback。isResolved 是一个标识,用来防止多次调用 resolve。callback 是一个数组,用来保存回调函数。

MyPromise 还有一个 then 函数,用来处理异步后的回调,能够链式异步调用。

这样子就实现了一个简单的 Promise,完整的代码戳 这里

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Promise源码实现比较复杂,这里我给你一个简化版的Promise源码示例,以帮助你理解其基本原理: ```javascript // Promise构造函数 function Promise(executor) { var self = this; self.value = null; // Promise的值 self.status = 'pending'; // Promise的状态 self.onResolvedCallbacks = []; // 存储成功回调函数的数组 self.onRejectedCallbacks = []; // 存储失败回调函数的数组 function resolve(value) { if (self.status === 'pending') { self.status = 'resolved'; self.value = value; self.onResolvedCallbacks.forEach(function (callback) { callback(value); }); } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.value = reason; self.onRejectedCallbacks.forEach(function (callback) { callback(reason); }); } } try { executor(resolve, reject); } catch (e) { reject(e); } } // then方法 Promise.prototype.then = function (onResolved, onRejected) { var self = this; var promise2; onResolved = typeof onResolved === 'function' ? onResolved : function (value) { return value; }; onRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason; }; if (self.status === 'resolved') { return promise2 = new Promise(function (resolve, reject) { try { var x = onResolved(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } if (self.status === 'rejected') { return promise2 = new Promise(function (resolve, reject) { try { var x = onRejected(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } if (self.status === 'pending') { return promise2 = new Promise(function (resolve, reject) { self.onResolvedCallbacks.push(function (value) { try { var x = onResolved(value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); self.onRejectedCallbacks.push(function (reason) { try { var x = onRejected(reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }); } }; // 解析Promise的状态 function resolvePromise(promise, x, resolve, reject) { var then; var thenCalledOrThrow = false; if (promise === x) { return reject(new TypeError('Chaining cycle detected for promise!')); } if (x instanceof Promise) { if (x.status === 'pending') { x.then(function (value) { resolvePromise(promise, value, resolve, reject); }, reject); } else { x.then(resolve, reject); } return; } if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) { try { then = x.then; if (typeof then === 'function') { then.call(x, function rs(y) { if (thenCalledOrThrow) return; thenCalledOrThrow = true; return resolvePromise(promise, y, resolve, reject); }, function rj(r) { if (thenCalledOrThrow) return; thenCalledOrThrow = true; return reject(r); }); } else { resolve(x); } } catch (e) { if (thenCalledOrThrow) return; thenCalledOrThrow = true; return reject(e); } } else { resolve(x); } } ``` 这只是一个简化版的Promise源码实现,真正的Promise实现要考虑更多的细节和边界情况。希望这个示例能帮助你更好地理解Promise的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值