这一次,彻底理解Promise源码思想

关于Promise的源码实现,网上有太多答案,我也看过很多资料,但都不是很明白。直到有一天我学完函数式编程之函子的概念,才对Promise源码有了更深刻的认识。今天,就让我们来重新认识一下Promise。

我们知道,Promise的诞生是为了解决“回调地狱”的问题,它用同步链式的方式去解决异步的嵌套回调。

啥?同步链式?这不就是我们上一节学习的函子的思想吗?如果对函子有所了解,那么再来学习Promise源码就比较容易理解了。接下来,我们探究一下函子和Promise有着怎样的关系。

实现一个简单的Promise函子

系统小说网 https://www.kuwx.net/

先来回顾一下函子Functor的链式调用:

class Functor{
       constructor (value) {
          this.value = value ;
       }      
       map (fn) {
         return Functor.of(fn(this.value))
       }
    }

Functor.of = function (val) {
     return new Functor(val);
}

Functor.of(100).map(add1).map(add1).map(minus10)

// var  a = Functor.of(100);
// var  b = a.map(add1);
// var  c = b.map(add1);
// var  d = c.map(minus10);

函子链式调用
函子的核心就是:每个函子Functor都是一个新的对象,这个对象的原型链上有 map 函数。通过 map 中传递进去的函数fn去处理函子保存的数据,用得到的值去生成新的函子。

等等...函子是同步链式,而Promise是异步链式。也就是说上面a的值是异步产生的,那我们该何如传入 this.value 值呢?

function executor(resolve){
  setTimeout(()=>{ resolve(100) },500)
}

我们模拟一下通过 setTimeout500 毫秒后拿到数据100。其实也很简单,我们可以传进去一个 resolve 回调函数去处理这个数据。

class MyPromise {
   constructor (executor) {
      let self = this;
      this.value = undefined;

      // 回调函数,用来赋值给 value
      function resolve(value){
          self.value = value;
      }
      executor(resolve)
   } 
}

var a = new MyPromise(executor);

解释一下上面的代码:我们将 executor 传入并立即执行,在 resolve 回调函数中我们能够拿到 value 值,我们定义 resolve 回调函数将 value 的值赋给 this.value。

这样我们就轻松的完成了 a 这个对象的赋值。由于是异步得到的,那么我们怎么用方法去处理这个数据呢?

根据函子的思想,在拿到数据之后,我们应该让 map 里传入的 fn 函数去处理数据。由于是异步处理, resolve 执行后才拿到数据,所以我们定义了一个 callback 函数,在 callback 里面执行 fn。最后把 fn 处理的结果交给下一个函子的 resolve 保存。

class MyPromise {
   constructor (executor) {
      let self = this;
      this.value = undefined;
      this.callback = null;
      // 回调函数,用来赋值给 value
      function resolve(value){
           self.value = value
           self.callback()  // 得到 value 之后,在 callback 里面执行 map 传入的 fn 函数处理数据
      }
      executor(resolve)
   } 
  
   map (fn) {
       let self = this;
       return new MyPromise((resolve) => {
          self.callback = function(){
              let data =  fn(self.value)   
              resolve(data)
           }
       })
   }    
}

new MyPromise(executor).map(add1).map(add1)

同时调用同一个Promise函子

Promise除了能链式调用,还能同时调用,比如:

var a = new MyPromise(executor);
var b = a.map(add);
var c = a.map(minus);

像上面这个同时调用a这个函子。你会发现,它实际上只执行了c。原因也很简单,b先给a的 callback 赋值,然后c又给a的 callback 赋值。所以把b给覆盖掉了就不会执行啦。解决这个问题很简单,我们只需要让callback变成一个数组就解决了。

class MyPromise {
   constructor (executor) {
      let self = this;
      this.value = undefined;
      this.callbacks = [];
      function resolve(value){
          self.value = value;
          self.callbacks.forEach(item => item())
      }
      executor(resolve)
   } 
  
   then (fn) {
       return new MyPromise((resolve) => {
          this.callbacks.push (()=>{
              let data =  fn(this.value) 
              console.log(data)         
              resolve(data)
           })
       })
   }    
}

var a = new MyPromise(executor);
var b = a.then(add).then(minus);
var c = a.then(minus);

我们定义了callbacks数组,每次的调用a的then方法时。都将其存到callbacks数组中。
当回调函数拿到值时,在resolve中遍历执行每个函数。
如果callbacks是空,forEach就不会执行,这也解决了之前把错的问题
然后我们进一步改了函子的名字为 MyPromise,将map改成then
简化了return中,let self = this;

增加reject回调函数

我们都知道,在异步调用的时候,我们往往不能拿到数据,返回一个错误的信息。这一小节,我们对错误进行处理。

class MyPromise {
  constructor (executor) {
    let self = this;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    function resolve(value){
      self.value = value;
      self.onResolvedCallbacks.forEach(item => item())
    }
    function reject(reason){
      self.reason = reason;
      self.onRejectedCallbacks.forEach(item => item());
    }
    executor(resolve, reject);
  } 
  then (fn,fn2) {
    return new MyPromise((resolve,reject) => {
      this.onResolvedCallbacks.push (()=>{
        let data =  fn(this.value) 
        console.log(data)         
        resolve(data)
      })
      this.onRejectedCallbacks.push (()=>{
        let reason =  fn2(this.reason) 
        console.log(reason)         
        reject(reason)
      })
    })
  }    
}

其实很简单,就是我们就是在 executor 多传递进去一个 reject
根据异步执行的结果去判断执行 resolve,还是 reject
然后我们在 MyPromise 为 reject 定义出和 resolve 同样的方法
然后我们在 then 的时候应该传进去两个参数,fn,fn2

这时候将executor函数封装到asyncReadFile异步读取文件的函数

function asyncReadFile(url){
  return new MyPromise((resolve,reject) => {
    fs.readFile(url, (err, data) => {
      if(err){ 
         console.log(err)
         reject(err)
      }else {
         resolve(data)
      }
    })
  })
}
var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(minus,mismanage);

这就是我们平时封装异步Promise函数的过程,这个过程有没有觉得在哪见过。仔细看下,asyncReadFile 不就是前面我们提到的柯里化。

增加Promise状态

我们定义进行中的状态为pending
已成功执行后为fulfilled
失败为rejected

class MyPromise {
  constructor (executor) {
    let self = this;
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    function resolve(value){
      if (self.status === 'pending') {
        self.status = 'fulfilled';
        self.value = value;
        self.onResolvedCallbacks.forEach(item => item())
      }
    }
    function reject(reason){
      if (self.status === 'pending') {
        self.status = 'rejected';  
        self.reason = reason;
        self.onRejectedCallbacks.forEach(item => item());
      }
    }
    executor(resolve, reject);
  } 
  then (fn,fn2) {
     return new MyPromise((resolve,reject) => {
      if(this.status === 'pending'){
        this.onResolvedCallbacks.push (()=>{
          let data =  fn(this.value) 
          console.log(data)         
          resolve(data)
        })
        this.onRejectedCallbacks.push (()=>{
          let reason =  fn2(this.reason) 
          console.log(reason)         
          reject(reason)
        })
      }
      if(this.status === 'fulfilled'){
          let x = fn(this.value)
          resolve(x)
      }
      if(this.status === 'rejected'){
          let x = fn2(this.value)
          reject(x)
      }
    })
  }    
}

var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(add,mismanage).then(add,mismanage);

最后,现在来看传进去的方法 fn(this.value) ,我们需要用上篇讲的Maybe函子去过滤一下。

Maybe函子优化

 then (onResolved,onRejected) {
     
     onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
     onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}

     return new MyPromise((resolve,reject) => {
      if(this.status === 'pending'){
        this.onResolvedCallbacks.push (()=>{
          let x =  onResolved(this.value) 
          resolve(x)
        })
        this.onRejectedCallbacks.push (()=>{
          let x =  onRejected(this.reason)
          reject(x)
        })
      }
      if(this.status === 'fulfilled'){
          let x = onResolved(this.value)
          resolve(x)
      }
      if(this.status === 'rejected'){
          let x = onRejected(this.value)
          reject(x)
      }
    })
  }    

Maybe函子很简单,对onResolved和onRejected进行一下过滤。

总结

Promise是一个很不好理解的概念,但总归核心思想还是函子。

同时,在函子的基础上增加了一些异步的实现。异步的实现是一个比较费脑细胞的点,把加粗的字体花点时间多思考思考,加油!

参考链接:函数式编程之Promise的奇幻漂流

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值