当前位置: 首页 > news >正文

promise原理就是这么简单

异步编程的几种形式:

回调函数形式:

function f1(callback){
    callback();
}
function f2(callback){
    callback();
}
function f3(callback){
    callback();
}
f1(f2(f3))
复制代码

这种方式实现异步编程优点是思路清晰,以串行的思考方式进行编程,缺点是形成回调地狱,过多的回调嵌套使得代码变得难以理解拆分和维护。

发布订阅模式

let dep = {
  list: [],
  on: function (fn) {
    list.push(fn);
  },
  emit: function () {
    this.list.forEach(event => {
      typeof event === 'function' ? event() : null;
    })
  }
};
复制代码

上面就是简易版的发布订阅模式:发布者存在一个数组list用于登记订阅者即异步执行的函数,等到一定条件下执行emit,订阅的异步函数都会执行。这就好比发布者售楼中心的拥有一个登记册,里面登记需要买房的所有订阅者,有的订阅者登记的是电话通知,有的订阅者登记的邮件通知,等到楼盘信息变化时会主动给每个订阅者执行相应的操作(执行对应的函数)

promise

promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)。如果还不熟悉promise用法的朋友,请参考Es6入门之promise对象,下面来分析promise的特点。

promise的特点:

  1. new Promise时需要传递一个executor执行器,执行器会立刻执行
  2. 执行器中传递了两个参数:resolve成功的函数、reject失败的函数,他们调用时可以接受任何值的参数value
  3. promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected,然后执行相应缓存队列中的任务
  4. promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected
  5. promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中
  6. promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行 (发布订阅)

promise基础版实现

下面针对这些特点来实现promise:

new Promise时需要传递一个executor执行器,执行器会立刻执行;执行器中传递了两个参数:resolve成功的函数、reject失败的函数,他们调用时可以接受任何值的参数value
function Promise (executor){
    function resolve(value){}
    function reject(value){}
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
}
var promise = new Promise((resolve,reject)=>{
    console.log('start');
})
复制代码
promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected
function Promise (executor) {
  var self = this;//resolve和reject中的this指向不是promise实例,需要用self缓存
  self.state = 'padding';
  self.value = '';//缓存成功回调onfulfilled的参数
  self.reson = '';//缓存失败回调onrejected的参数
  self.onResolved = []; // 专门存放成功的回调onfulfilled的集合
  self.onRejected = []; // 专门存放失败的回调onrejected的集合
  function resolve (value) {
    if(self.state==='padding'){
      self.state==='resolved';
      self.value=value;
      self.onResolved.forEach(fn=>fn())
    }
  }
  function reject (reason) {
    self.state = 'rejected';
    self.value = reason;
    self.onRejected.forEach(fn=>fn())
  }
  try{
    executor(resolve,reject)
  }catch(e){
    reject(e)
  }
}
复制代码
promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected;
promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中;
promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行(发布订阅)
Promise.prototype.then=function (onfulfilled,onrejected) {
  var self=this;
  if(this.state==='resolved'){
    onfulfilled(self.value)
  }
  if(this.state==='rejected'){
    onrejected(self.value)
  }
  if(this.state==='padding'){
    this.onResolved.push(function () {
      onfulfilled(self.value)
    })
  }
}
复制代码

then方法的特点:

以上只是实现了promise的基本功能,但是还缺少then的链式调用,then函数参数onfulfilled,onrejected缺省处理,链式调用返回值多种情况的处理,下面分析then方法的特点:

  1. 因为promise状态确定后就是不能更改,所以每次promise执行then后都会返回一个新的promise而不是this,那么状态永远为resolve或jeject,将存在异步调用
  2. onfulfilled或onrejected是一个可选参数,需要做没有传递时的处理
  3. 如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调
  4. 如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中
  5. 如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次
  6. 如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败

promise优化版实现

因为promise状态确定后就是不能更改,所以每次promise执行then后都会返回一个新的promise而不是this,那么状态永远为resolve或jeject,将存在异步调用;onfulfilled或onrejected是一个可选参数,需要做没有传递时的处理
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;//onfulfilled缺省处理
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};//onrejected缺省处理
  var self=this,promise2=new Promise(function(resolve,reject){//返回一个promise
    if(this.state==='resolved'){
    try{
        onfulfilled(self.value);//里面会执行resolve    
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='rejected'){
    try{
        onrejected(self.value);
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='padding'){//将执行过程缓存
   self.onResolved.push(function () {
        try{
            onfulfilled(self.value);
        }catch(e){
           reject(e)
        }
    });
    self.onRejected.push(function () {
        try{
            onrejected(self.value);
        }catch(e){
            reject(e)
        }
    })
  }  
  })
  return promise2;
}
复制代码
如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调;
如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中;
如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次;
如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败,所以需要对onfulfilled或onrejected的返回值进行处理。
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  var self=this,
    res=null,//用来缓存onfulfilled或onrejected的返回值
    promise2=new Promise(function(resolve,reject){
    if(this.state==='resolved'){
    try{
        res = onfulfilled(self.value);//得到onfulfilled的返回值
        resolvePromise(promise2,res,resolve,reject);//返回值的处理函数
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='rejected'){
    try{
        res = onrejected(self.value);//得到onrejected的返回值
        resolvePromise(promise2,res,resolve,reject);
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='padding'){
   self.onResolved.push(function () {
        try{
            res = onfulfilled(self.value);
            resolvePromise(promise2,res,resolve,reject);
        }catch(e){
           reject(e)
        }
    });
    self.onRejected.push(function () {
        try{
            res = onrejected(self.value);
            resolvePromise(promise2,res,resolve,reject);
        }catch(e){
            reject(e)
        }
    })
  }  
  })
  return promise2;
}
function resolvePromise(promise,res,resolve,reject) {
  if(promise===res){//防止循环引用
    return reject(new TypeError('循环引用'))
  }
  let called;//防止重复执行
  if(res!==null&&(typeof res==='function'||typeof res ==='object')){
    try {//防止promise执行报错
      let then=res.then;//判断是否promise就判断是否存在then方法
      if(typeof then ==='function'){//如果返回的是promise,只需要在返回的promise的then方法中下一步需要执行的函数
        then.call(res,(res2)=>{
          if (called) return;
          called = true;
          resolvePromise(promise,res2,resolve,reject);//如果是promise继续递归执行,直到不是promise,依次执行外层的resolve,让promise状态改变
        },)
      }else{//如果不是promise,有可能是undefine、onfulfilled或onrejected的返回的普通值,就直接将这个值返回,将外层的promise状态改变
        if (called) return;
        called = true;
        resolve(then)
      }
    }catch(e){
      if (called) return;
      called = true;
      reject(e)
    }
  }else{
    resolve(res)
  }
};
复制代码
promise.then属于异步微任务,then中的方法,必须等到所有的同步任务执行完才执行,宏任务和微任务可以查看EventLoop其实如此简单
console.log(1);
var promise=new Promise(function(resolve,reject){
    resolve('a');
})
promise.then(function(value){
    console.log(value)
})
console.log(2);
// 1 2 'a'
复制代码
//由于promise有内部的机制实现微任务,所以这里使用setTimeout代替
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  var self=this,
    res=null,
    promise2=new Promise(function(resolve,reject){
    if(this.state==='resolved'){
        setTimeout(()=>{
            try{
                res = onfulfilled(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }
        })
    }
  if(this.state==='rejected'){
        setTimeout(()=>{
            try{
                res = onrejected(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }  
        })
    }
  if(this.state==='padding'){
    self.onResolved.push(function () {
        setTimeout(()=>{
            try{
                res = onfulfilled(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }
        })
    });
    self.onRejected.push(function () {
        setTimeout(()=>{
            try{
                res = onrejected(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }  
        })
    })
  }  
  })
  return promise2;
}
复制代码

promise.catch会捕获到没有捕获的异常;

Promise.resolve会返回一个状态位rsolved的promise;

Promise.reject会返回一个状态位rsolved的promise;

Promise.all会在所有的promise resolved后执行回调,Promise.race只要有一个promise resolved就执行回调。

promise.catch将所有的错误在promise实例的下一个then中返回
Promise.prototype.catch = function (onrejected) {
  return this.then(null, onrejected)
};
复制代码
Promise.reject和Promise.reject
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
};
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    resolve(value);
  })
};
复制代码
Promise.all和Promise.race
//在每个promise的回调中添加一个判断函数processData(就是在当前的promise.then中添加),每个promise状态改变后让index++,直到和promises的个数相等就执行回调
Promise.all=function (promises) {
  return new Promise((resolve,reject)=>{
    let results=[],i=0;
    for(let i=0;i<promises.length;i++){
      let p=promises[i];
      p.then((data)=>{
        processData(i,data)
      },reject)
    }
    function processData (index,data) {
      results[index]=data;
      if(++i==promises.length){
        resolve(results)
      }
    }
  })
};
//在每个promise的回调中添加一个resolve(就是在当前的promise.then中添加),有一个状态改变,就让race的状态改变
Promise.race=function (promises) {
  return new promises((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      let p=promises[i];
      p.then(resolve,reject)
    }
  })
};
复制代码

generator + co

Generator函数可以通过yield暂停执行和next恢复执行,所以可以封装一个函数来自动执行next函数而使Generator完成异步任务。

let fs = require('mz/fs');
function * read() {
  let age = yield fs.readFile('./name.txt','utf8');
  let adress = yield fs.readFile(age,'utf8');
  let r = yield fs.readFile(adress,'utf8');
  return r;
}
function co(it) {//it为一个generator对象
//返回一个promise并且执行generator对象的next
return new Promise((resolve,reject)=>{
    function next(data) {
        //获取前一个异步函数的返回值,其必须为promise
        let { value, done } = it.next(data);  
            if(!done){
            //返回promise的then方法中添加generator的next
            //前一个异步函数执行成功会使返回的promise成resolved
            //然后执行generator的next,递归依次类推
            value.then(data=>{  
                next(data)
            }, reject);
        }else{
            resolve(value);
        }
    }
        next();
  })
}
co(read()).then(data=>{
  console.log(data);
},err=>{
  console.log(err);
});
复制代码

async和awit

async+awit等于generator+co,而co中实现generator自动化是基于Promise,所以awit会使用new Promise形式。

结语

如果以上有任何错误之处,希望提出并请指正,如果对promise使用还不清楚的朋友,请参考Es6入门之promise对象,本文参考:

  1. Promise A+规范
  2. Es6入门之promise对象
  3. Promise 源码
  4. 教你一步步实现promise

相关文章:

  • EXE文件执行过程中发生了什么?
  • MathExam小学一二年级计算题生成器V1.0
  • 建设银行无人银行开业,铁饭碗是属于程序员的
  • Java 集合系列-第八篇-Map架构
  • Redhat7.0下部署NFS服务器
  • 网络,NFS
  • 服务器目录权限
  • LAMP搭建
  • 自动生成指定特征的数独题目(未完待续)
  • 学习python必备的学习网站
  • Linux服务器性能评估
  • Synchronized与Lock的底层实现解析
  • ES6数组的扩展----Array.from()和Array.of()
  • jdk动态代理和cglib动态代理的区别
  • 设计模式-结构型模式,python组合模式
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • axios 和 cookie 的那些事
  • C++类中的特殊成员函数
  • go append函数以及写入
  • javascript面向对象之创建对象
  • Java读取Properties文件的六种方法
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • PHP的类修饰符与访问修饰符
  • PHP那些事儿
  • Rancher-k8s加速安装文档
  • React中的“虫洞”——Context
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 动态魔术使用DBMS_SQL
  • 规范化安全开发 KOA 手脚架
  • 区块链将重新定义世界
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 最简单的无缝轮播
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #Java第九次作业--输入输出流和文件操作
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (待修改)PyG安装步骤
  • (篇九)MySQL常用内置函数
  • (转)LINQ之路
  • .CSS-hover 的解释
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .NET MVC之AOP
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .net 无限分类
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .NET6 开发一个检查某些状态持续多长时间的类
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)
  • .NET基础篇——反射的奥妙
  • @Autowired 与@Resource的区别
  • @GetMapping和@RequestMapping的区别
  • [20170705]diff比较执行结果的内容.txt
  • [acm算法学习] 后缀数组SA
  • [ai笔记3] ai春晚观后感-谈谈ai与艺术