谈谈你对Promise的理解

news/2024/9/6 6:08:33 标签: javascript

背景

Promise是为了解决Javascript回调嵌套过多导致回调地狱(callbackhell)而产生的。目前已被纳入了es2015规范,主流浏览器都支持Promise。为了在工作中更好的运用Promise,我们需要理解Promise规范与内部实现机制,下面我们来手动实现一个Promise。

Promise/A+规范

在写代码之前让我们先了解下 Promise/A+规范。
一个promise有三种状态:

  • pending:表示初始状态,可以转移到 fullfilled 或者 rejected 状态
  • fulfilled:表示操作成功,不可转移状态
  • rejected:表示操作失败,不可转移状态
  • 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个promise:

借用这张来自MDN的流程图我们可以清晰的看到 Promise 状态的流转过程。

简单版

下面我们来实现一个简单版的 Promise:

javascript copyable">function Promise1(executor){
  let self = this;
	self.status = 'pending';
  self.value = undefined;
  self.reason = undefined;
  
  function resolve(value) {
  	if(self.status==='pending'){
    	self.status = 'fullfilled';
      self.value = value;
    }
  }
  function reject(reason) {
  	if(self.status==='pending') {
    	self.status = 'rejected';
      self.reason = reason;
    }
  }
  
  try{
  	executor(resolve,reject);
  }catch(e) {
  	reject(e);
  }
}

Promise1.prototype.then = function(onFullfilled,onRejected) {
	if(this.status==='fullfilled') {
  	onFullfilled(this.value);
  }
  if(this.status==='rejected') {
  	onRejected(this.reason);
  }
}
//测试
let p= new Promise1(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
复制代码

支持异步

现在,我们实现了最简单的 Promise。以上版本的Promise是存在很多问题的。为什么呢?最大的问题是它不支持异步,然而在现实中,Promise绝大多数使用场景都是异步。让我们来为 Promise 加入异步功能。

javascript copyable">const PENDING = 'pending';
const FULFILLED = 'fullfilled';
const REJECTED = 'rejected';
function Promise1(executor){
  let self = this;
	self.status = PENDING;
  self.value = undefined;
  self.reason = undefined;
  self.fullfilledCallbacks = [];
  self.rejectedCallbacks = [];

  function resolve(value) {
    if(value instanceof Promise) {
    	value.then(resolve,reject);
    }
  	setTimeout(function(){
    	if(self.status===PENDING){
        self.status = FULFILLED;
        self.value = value;
        self.fullfilledCallbacks.forEach(function(cb){
        	cb(self.value)
        })
      }
    })
  }
  function reject(reason) {
  	setTimeout(function(){
    	if(self.status===PENDING) {
        self.status = REJECTED;
        self.reason = reason;
        self.rejectedCallbacks.forEach(function(cb){
        	cb(self.reason);
        })
      }
    })
  }

  try{
  	executor(resolve,reject);
  }catch(e) {
  	reject(e);
  }
}

Promise1.prototype.then = function(onFulfilled,onRejected) {
  let self = this;
  return new Promise1(function(resolve,reject){
  		function success(value) {
        let _value = (typeof onFulfilled === 'function' && onFulfilled(value)) || value;
        resolve(_value)
      }
      function error(reason) {
        let _reason = (typeof onRejected === 'function' && onRejected(reason)) || reason;
        reject(_reason);
      }
    	if(self.status==PENDING) {
      	self.fullfilledCallbacks.push(success);
        self.rejectedCallbacks.push(error);
      } else if(self.status==FULLFILLED){
      	success.call(this,this.value)
      } else if(self.status==REJECTED) {
      	error.call(this,this.reason);
      }
  })
}

复制代码

以上代码中,我们做了如下更改:

  1. 将 Promise 三个状态定义为常量,方便维护
  2. 对于 Promise resolve和reject 函数执行加入异步处理
  3. 在Promise.then中返回新的Promise对象,使Promise可以支持链式调用

错误处理以及静态方法

下面让我们来为Promise 添加错误处理以及静态方法:

javascript copyable">//错误处理
Promise1.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
}

//返回fullfilled Promise对象
Promise1.resolve = function (value) {
    return new Promise1(resolve => {
        resolve(value);
    });
}
//返回 rejected Promise 对象
Promise1.reject = function (reason) {
    return new Promise1((resolve, reject) => {
        reject(reason);
    });
}
//Promise.all方法
Promise1.all = function(promises) {
    function gen(length, resolve) {
        let count = 0;
        let values = [];
        return function(i, value) {
            values[i] = value;
            if (++count === length) {
                resolve(values);
            }
        }
    }
    return new Promise1((resolve, reject) => {
        let done = gen(promises.length, resolve);
        promises.forEach((promise, index) => {
            promise.then((value) => {
                done(index, value)
            }, reject)
        })
    })
}
//Promise.race方法
Promise1.race = function(promises) {
    return new Promise1((resolve, reject) => {
        promises.forEach((promise, index) => {
           promise.then(resolve, reject);
        });
    });
}

复制代码

这里有个问题,就是在当我们console.log(Promise1.resolve('a'))的时候,我发现打印出来的状态竟然是 pending状态,我猜想原因是应该是resolve中函数异步执行,在当我们console的时候setTimeout中代码未执行,所以我给出的解决方法是将状态变化与赋值移到setTimeout外面,这样就不会产生刚才的问题了,更改后代码长这样:

javascript copyable">function resolve(value) {
    if(value instanceof Promise) {
    	value.then(resolve,reject);
    }
  	self.status = FULFILLED;
  	self.value = value;
  	setTimeout(function(){
    	if(self.status===PENDING){
        self.fullfilledCallbacks.forEach(function(cb){
        	cb(self.value)
        })
      }
    })
  }

function reject(reason) {
    self.status = REJECTED;
    self.reason = reason;
  	setTimeout(function(){
    	if(self.status===PENDING) {
        self.rejectedCallbacks.forEach(function(cb){
        	cb(self.reason);
        })
      }
    })
  }
复制代码

总结

经过以上实践,我们成功的手写了一个功能完备的 Promise。这里给我的最大启发是如果我们想学习一个东西,必须深入到它的底层,了解它的运行原理与具体实现方法,并且可以造一个简单的轮子,这样才算我们掌握了该知识点。从前的我对于这一点没有关注的太多,导致在用某个知识点时只是掌握的它的表层用法,在高级一点的使用场景时完全不会运用。以后我会更加注重源码方面的学习,弥补我这方面的不足。

转载于:https://juejin.im/post/5ce767f2f265da1bd522a8c0


http://www.niftyadmin.cn/n/1772928.html

相关文章

为什么有人说 996 却没人说初中高中生的 596?

昨天在知乎上看到这么一个问题:为什么有人说 996 却没人说初中高中生的 596?而就在前几天和朋友聊天的时候,正好讨论过这个问题,员工的时候 996 ,当学生的时候,596,甚至 5126 都不为过。为什么会…

kali linux安全***

kali linux安全***测试 搭建测试环境许多提供安全服务的机构会使用一些术语,如安全审计、网络或风险评估、以及测试。这些术语在含义上有一些重叠,从定义上来看,审计是对系统或应用的量化的技术评估。安全评估意为对风险的评测,是…

如何用 MacBook 提高工作效率 【配置篇】

上一篇文章从应用(application)的选择上推荐了一些我常用的应用或工具,这篇主要从配置(config)和思路、原则上提高工作效率。总而言之,我们的目的只有一个,帮助尽快完成工作,能不加班…

Linux学习的常用命令

Linux学习的常用命令 公司一直用的服务器都是window服务器,自己也好久没接触Linux了,命令忘得差不多了,之前也是菜鸡水平,就布置个服务啥的,正好最近不怎么忙,自己学习下,记录下常用的命令以便翻…

2019春第一次课程设计实验报告

1.实验项目名称 飞机游戏2.实验项目功能描述 实现一个飞机游戏,主要包括新式子弹,敌机移动,击中敌机,清屏。3.项目模块介绍 清屏 void HideCuresor() {CONSOLE_CURSOR_INFO cursor_info{1,0};SetConsoleCursorInfo(GetStdHandle(S…

1090. Highest Price in Supply Chain (25)BFS,DFS

A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone involved in moving a product from supplier to customer. Starting from one root suppli…

关于继承的那些事!

第二阶段 JAVA面向对象 第二章 继承 其实在我们面向对象这一阶段的学习中,我们就是围绕Java的三大特性(封装、继承、多态)来讲解的,在上一掌内容中我们讲解了一部分封装的有关知识,今天我们来重点学习继承的学习。 2.1…

1091. Acute Stroke (30)

One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the results of image analysis in which the core regions are identified in each MRI slice, your job is to calculate the volume of the stroke core. Input Speci…