当前位置:   article > 正文

promise原理就是这么简单

onrejected = typeof (onrejected) == 'function' ? onrejected

异步编程的几种形式:

回调函数形式:

  1. function f1(callback){
  2. callback();
  3. }
  4. function f2(callback){
  5. callback();
  6. }
  7. function f3(callback){
  8. callback();
  9. }
  10. f1(f2(f3))
  11. 复制代码

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

发布订阅模式

  1. let dep = {
  2. list: [],
  3. on: function (fn) {
  4. list.push(fn);
  5. },
  6. emit: function () {
  7. this.list.forEach(event => {
  8. typeof event === 'function' ? event() : null;
  9. })
  10. }
  11. };
  12. 复制代码

上面就是简易版的发布订阅模式:发布者存在一个数组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
  1. function Promise (executor){
  2. function resolve(value){}
  3. function reject(value){}
  4. try{
  5. executor(resolve,reject);
  6. }catch(e){
  7. reject(e);
  8. }
  9. }
  10. var promise = new Promise((resolve,reject)=>{
  11. console.log('start');
  12. })
  13. 复制代码
promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected
  1. function Promise (executor) {
  2. var self = this;//resolve和reject中的this指向不是promise实例,需要用self缓存
  3. self.state = 'padding';
  4. self.value = '';//缓存成功回调onfulfilled的参数
  5. self.reson = '';//缓存失败回调onrejected的参数
  6. self.onResolved = []; // 专门存放成功的回调onfulfilled的集合
  7. self.onRejected = []; // 专门存放失败的回调onrejected的集合
  8. function resolve (value) {
  9. if(self.state==='padding'){
  10. self.state==='resolved';
  11. self.value=value;
  12. self.onResolved.forEach(fn=>fn())
  13. }
  14. }
  15. function reject (reason) {
  16. self.state = 'rejected';
  17. self.value = reason;
  18. self.onRejected.forEach(fn=>fn())
  19. }
  20. try{
  21. executor(resolve,reject)
  22. }catch(e){
  23. reject(e)
  24. }
  25. }
  26. 复制代码
promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected;
promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中;
promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行(发布订阅)
  1. Promise.prototype.then=function (onfulfilled,onrejected) {
  2. var self=this;
  3. if(this.state==='resolved'){
  4. onfulfilled(self.value)
  5. }
  6. if(this.state==='rejected'){
  7. onrejected(self.value)
  8. }
  9. if(this.state==='padding'){
  10. this.onResolved.push(function () {
  11. onfulfilled(self.value)
  12. })
  13. }
  14. }
  15. 复制代码
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是一个可选参数,需要做没有传递时的处理
  1. Promise.prototype.then=function (onfulfilled,onrejected) {
  2. onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;//onfulfilled缺省处理
  3. onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};//onrejected缺省处理
  4. var self=this,promise2=new Promise(function(resolve,reject){//返回一个promise
  5. if(this.state==='resolved'){
  6. try{
  7. onfulfilled(self.value);//里面会执行resolve
  8. }catch(e){
  9. reject(e);
  10. }
  11. }
  12. if(this.state==='rejected'){
  13. try{
  14. onrejected(self.value);
  15. }catch(e){
  16. reject(e);
  17. }
  18. }
  19. if(this.state==='padding'){//将执行过程缓存
  20. self.onResolved.push(function () {
  21. try{
  22. onfulfilled(self.value);
  23. }catch(e){
  24. reject(e)
  25. }
  26. });
  27. self.onRejected.push(function () {
  28. try{
  29. onrejected(self.value);
  30. }catch(e){
  31. reject(e)
  32. }
  33. })
  34. }
  35. })
  36. return promise2;
  37. }
  38. 复制代码
如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调;
如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中;
如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次;
如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败,所以需要对onfulfilled或onrejected的返回值进行处理。
  1. Promise.prototype.then=function (onfulfilled,onrejected) {
  2. onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
  3. onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  4. var self=this,
  5. res=null,//用来缓存onfulfilled或onrejected的返回值
  6. promise2=new Promise(function(resolve,reject){
  7. if(this.state==='resolved'){
  8. try{
  9. res = onfulfilled(self.value);//得到onfulfilled的返回值
  10. resolvePromise(promise2,res,resolve,reject);//返回值的处理函数
  11. }catch(e){
  12. reject(e);
  13. }
  14. }
  15. if(this.state==='rejected'){
  16. try{
  17. res = onrejected(self.value);//得到onrejected的返回值
  18. resolvePromise(promise2,res,resolve,reject);
  19. }catch(e){
  20. reject(e);
  21. }
  22. }
  23. if(this.state==='padding'){
  24. self.onResolved.push(function () {
  25. try{
  26. res = onfulfilled(self.value);
  27. resolvePromise(promise2,res,resolve,reject);
  28. }catch(e){
  29. reject(e)
  30. }
  31. });
  32. self.onRejected.push(function () {
  33. try{
  34. res = onrejected(self.value);
  35. resolvePromise(promise2,res,resolve,reject);
  36. }catch(e){
  37. reject(e)
  38. }
  39. })
  40. }
  41. })
  42. return promise2;
  43. }
  44. function resolvePromise(promise,res,resolve,reject) {
  45. if(promise===res){//防止循环引用
  46. return reject(new TypeError('循环引用'))
  47. }
  48. let called;//防止重复执行
  49. if(res!==null&&(typeof res==='function'||typeof res ==='object')){
  50. try {//防止promise执行报错
  51. let then=res.then;//判断是否promise就判断是否存在then方法
  52. if(typeof then ==='function'){//如果返回的是promise,只需要在返回的promise的then方法中下一步需要执行的函数
  53. then.call(res,(res2)=>{
  54. if (called) return;
  55. called = true;
  56. resolvePromise(promise,res2,resolve,reject);//如果是promise继续递归执行,直到不是promise,依次执行外层的resolve,让promise状态改变
  57. },)
  58. }else{//如果不是promise,有可能是undefine、onfulfilled或onrejected的返回的普通值,就直接将这个值返回,将外层的promise状态改变
  59. if (called) return;
  60. called = true;
  61. resolve(then)
  62. }
  63. }catch(e){
  64. if (called) return;
  65. called = true;
  66. reject(e)
  67. }
  68. }else{
  69. resolve(res)
  70. }
  71. };
  72. 复制代码
promise.then属于异步微任务,then中的方法,必须等到所有的同步任务执行完才执行,宏任务和微任务可以查看EventLoop其实如此简单
  1. console.log(1);
  2. var promise=new Promise(function(resolve,reject){
  3. resolve('a');
  4. })
  5. promise.then(function(value){
  6. console.log(value)
  7. })
  8. console.log(2);
  9. // 1 2 'a'
  10. 复制代码
  1. //由于promise有内部的机制实现微任务,所以这里使用setTimeout代替
  2. Promise.prototype.then=function (onfulfilled,onrejected) {
  3. onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
  4. onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  5. var self=this,
  6. res=null,
  7. promise2=new Promise(function(resolve,reject){
  8. if(this.state==='resolved'){
  9. setTimeout(()=>{
  10. try{
  11. res = onfulfilled(self.value);
  12. resolvePromise(promise2,res,resolve,reject);
  13. }catch(e){
  14. reject(e);
  15. }
  16. })
  17. }
  18. if(this.state==='rejected'){
  19. setTimeout(()=>{
  20. try{
  21. res = onrejected(self.value);
  22. resolvePromise(promise2,res,resolve,reject);
  23. }catch(e){
  24. reject(e);
  25. }
  26. })
  27. }
  28. if(this.state==='padding'){
  29. self.onResolved.push(function () {
  30. setTimeout(()=>{
  31. try{
  32. res = onfulfilled(self.value);
  33. resolvePromise(promise2,res,resolve,reject);
  34. }catch(e){
  35. reject(e);
  36. }
  37. })
  38. });
  39. self.onRejected.push(function () {
  40. setTimeout(()=>{
  41. try{
  42. res = onrejected(self.value);
  43. resolvePromise(promise2,res,resolve,reject);
  44. }catch(e){
  45. reject(e);
  46. }
  47. })
  48. })
  49. }
  50. })
  51. return promise2;
  52. }
  53. 复制代码
promise.catch会捕获到没有捕获的异常;
Promise.resolve会返回一个状态位rsolved的promise;
Promise.reject会返回一个状态位rsolved的promise;
Promise.all会在所有的promise resolved后执行回调,Promise.race只要有一个promise resolved就执行回调。
promise.catch将所有的错误在promise实例的下一个then中返回
  1. Promise.prototype.catch = function (onrejected) {
  2. return this.then(null, onrejected)
  3. };
  4. 复制代码
Promise.reject和Promise.reject
  1. Promise.reject = function (reason) {
  2. return new Promise((resolve, reject) => {
  3. reject(reason)
  4. })
  5. };
  6. Promise.resolve = function (value) {
  7. return new Promise((resolve, reject) => {
  8. resolve(value);
  9. })
  10. };
  11. 复制代码
Promise.all和Promise.race
  1. //在每个promise的回调中添加一个判断函数processData(就是在当前的promise.then中添加),每个promise状态改变后让index++,直到和promises的个数相等就执行回调
  2. Promise.all=function (promises) {
  3. return new Promise((resolve,reject)=>{
  4. let results=[],i=0;
  5. for(let i=0;i<promises.length;i++){
  6. let p=promises[i];
  7. p.then((data)=>{
  8. processData(i,data)
  9. },reject)
  10. }
  11. function processData (index,data) {
  12. results[index]=data;
  13. if(++i==promises.length){
  14. resolve(results)
  15. }
  16. }
  17. })
  18. };
  19. //在每个promise的回调中添加一个resolve(就是在当前的promise.then中添加),有一个状态改变,就让race的状态改变
  20. Promise.race=function (promises) {
  21. return new promises((resolve,reject)=>{
  22. for(let i=0;i<promises.length;i++){
  23. let p=promises[i];
  24. p.then(resolve,reject)
  25. }
  26. })
  27. };
  28. 复制代码

generator + co

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

  1. let fs = require('mz/fs');
  2. function * read() {
  3. let age = yield fs.readFile('./name.txt','utf8');
  4. let adress = yield fs.readFile(age,'utf8');
  5. let r = yield fs.readFile(adress,'utf8');
  6. return r;
  7. }
  8. function co(it) {//it为一个generator对象
  9. //返回一个promise并且执行generator对象的next
  10. return new Promise((resolve,reject)=>{
  11. function next(data) {
  12. //获取前一个异步函数的返回值,其必须为promise
  13. let { value, done } = it.next(data);
  14. if(!done){
  15. //返回promise的then方法中添加generator的next
  16. //前一个异步函数执行成功会使返回的promise成resolved
  17. //然后执行generator的next,递归依次类推
  18. value.then(data=>{
  19. next(data)
  20. }, reject);
  21. }else{
  22. resolve(value);
  23. }
  24. }
  25. next();
  26. })
  27. }
  28. co(read()).then(data=>{
  29. console.log(data);
  30. },err=>{
  31. console.log(err);
  32. });
  33. 复制代码

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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/876471
推荐阅读
  

闽ICP备14008679号