热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Promise原理与实现探究的一种思路

写在前面这个文章,展现的是一个实现Promise的思路,以及如何发现和处理问题的情境。从现有的Promise分析如果我们想要自己实现一个简单的Promise,那现有规范规定的Promise肯

写在前面


这个文章,展现的是一个实现Promise的思路,以及如何发现和处理问题的情境。

从现有的Promise分析


如果我们想要自己实现一个简单的Promise,那现有规范规定的Promise肯定是我们最好的参照。

我们先看下Promise怎么使用:

var promise1 = new Promise(function(resolve, reject){
// 成功后的TODO
resolve(value);
// 失败后的TODO
reject(err);
})

来看下返回的promise1是什么,以及它的结构是怎么样的:

image

再进行一些具体操作

var promise1 = new Promise(function(resolve, reject) {
resolve('zqz')
})

promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})

// => 'zqz'

11

var promise1 = new Promise(function(resolve, reject) {
reject('出现异常')
})
promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})

// => '出现异常'

22

从Promise的 使用方式上 和 实例 可以看到哪些东西:

  • Promise是一个构造函数
  • Promise包含一个参数,这个参数类型是一个_匿名函数_
  • 匿名函数包括2个形参,分别是 rejectresolve
  • 这两个形参类型是 函数 ,且 rejectresolve 都有一个参数, 参数类型不限定
  • 实例 是个 Promise
  • 实例的 原型 上挂载了 2个方法,分别是 thencatch,同时then可以有多个,所以需要一个回掉函数队列
  • 实例上 有2个属性,分别是 PromiseStatusPromiseValue
  • Promise根据定义 PromiseStatus 需要有 3种状态,分别是 pending, fulfilledrejected

根据上面的分析情况,我们先简单的来构造一个雏形。

function Promise(fn) {
this.PromiseStatus = 'pending';
this.PromiseValue = '';

this.resolvedCb = [];
this.rejectedCb = [];

var self = this;

var resolve = function (result) {
// 判断状态
if (self.PromiseStatus === 'pending') {
self.PromiseStatus = 'resolved';
self.PromiseValue = result;
// resolvedCb 队列依次执行
for (var i = 0;i < self.resolvedCb.length; i++) {
self.resolvedCb[i](result)
}
}
}

var reject = function (err) {
// 判断状态
if (self.PromiseStatus === 'pending') {
self.PromiseStatus = 'rejected';
self.PromiseValue = err;

// rejectedCb 队列依次执行
for (var i = 0;i < self.rejectedCb.length; i++) {
self.rejectedCb[i](result)
}
}
}

// 错误处理 -> rejected
try {
fn(resolve, reject)
} catch(e) {
reject(e)
}

}

当然这还不够,因为重要的两个功能thencatch还没有实现。

从现有的 then 分析


分析下then的使用

promise1.then(function(value){
// todo
return value;
})
.then(function(value1){
// todo
return value1;
})
.then(function(value2){
// todo
return value2;
})
  • promise1 返回的值 需要塞到第一个then中函数的value上
  • 链式调用,多次调用
  • then返回的是一个新的Promise
  • then可以接受2个函数作为参数,一个是成功函数,一个是失败函数
  • return 的值 直接作为下个 then 中匿名函数的入参

根据Promise返回的实例,我们可看出来then是挂载在 Promise原型链上。

我们先实现一个大体的框架:

Promise.prototype.then = function (handleSuccess, handleFail) {
var self = this;
var PromiseStatus = this.PromiseStatus;

if(typeof handleSuccess === 'function') {
handleSuccess = handleSuccess;
} else {
handleSuccess = function (result) {}
}

if(typeof handleFail === 'function') {
handleFail = handleFail;
} else {
handleFail = function (err) {}
}

if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(handleSuccess);
self.rejectedCb.push(handleFail);
})
}

if(PromiseStatus === 'resolved') {
return new Promise(function(resolve, reject) {
var result = handleSuccess(self.PromiseValue);
resolve(result);
})
}

if(PromiseStatus === 'rejected') {
return new Promise(function(resolve, reject) {
var result = handleFail(self.PromiseValue);
reject(result);
})
}

}

我们先用一下,看下是否符合期望

方式一(无异步操作):

function promise1() {
return new Promise(function(resolve, reject){
console.log('执行promise1')
resolve('zqz');
})
}

promise1().then(function(result){
console.log('执行1', 'result:'+result)
return result + '11';
})
.then(function(result){
console.log('执行2', 'result:'+result)
return result + '22';
})
// => 执行promise1
// => 执行1 result:zqz
// => 执行2 result:zqz11
// => Promise {PromiseStatus: "resolved", PromiseValue: "zqz1122", resolvedCb: Array(0), rejectedCb: Array(0)}

这样使用没有问题!

方式二(有异步操作):

function promise1() {
return new Promise(function(resolve, reject){
// 异步操作
setTimeout(function(){
console.log('执行promise1')
resolve('zqz');
},1000)

})
}

promise1().then(function(result){
console.log('执行1', 'result:'+result)
return result + '11';
})
.then(function(result){
console.log('执行2', 'result:'+result)
return result + '22';
})
// => 执行promise1
// => 执行1 result:zqz

一旦出现异步操作,就有问题!很明显,Promise的主要作用就是控制异步操作的执行顺序。

肯定是哪里有问题,我们来分析一下,异步的时候 有哪些 不同

  • 当有异步操作(xhr,setTimeout等)的时候,这时候PromiseStatuspending状态

在来看下我们在pending时候的处理

...
// 异步时
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
// 这里只是将函数塞入队列,然后就没有然后来。。。这是有问题的
self.resolvedCb.push(handleSuccess);
self.rejectedCb.push(handleFail);
})
}
...

这时候我们的两个数组:resolvedCbrejectedCb就发挥作用了,由于我们不知道异步什么时候结束,但是我们可以根据他们定义的先后顺序注入到 队列 中,然后根据 顺序 依次执行,这样也就保证了异步操作的执行顺序。

if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
// 一个个的塞入队列
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
resolve(res);
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
reject(er);
})
})
}

这时候我们用多个异步操作来测试一下

function async1() {
return new Promise(function(resolve, reject){
// 异步操作
setTimeout(function(){
console.log('执行async1')
resolve('zqz1');
},3000)
})
}
function async2() {
return new Promise(function(resolve, reject){
// 异步操作
setTimeout(function(){
console.log('执行async2')
resolve('zqz2');
},1000)
})
}
function async3() {
return new Promise(function(resolve, reject){
// 异步操作
setTimeout(function(){
console.log('执行async3')
resolve('zqz3');
},2000)
})
}

// return 一个新的promise
async1().then(function(result){
console.log('result = ' + result)
return async2();
}).then(function(result){
console.log('result = ' + result)
return async3();
}).then(function(result){
console.log('result = ' + result)
return result;
})

// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 执行async1
// => result = zqz1
// => result = [object Object]
// => result = [object Object]
// => 执行async2
// => 执行async3

这里有两个问题:

  • 返回promise的时候,执行顺序有问题
  • 返回promise的时候,无法进行值的传递

我们再来分析下,着重看下下面这块代码

...
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
// 这里返回的res有可能是promise,但是我们没有做处理
var res = handleSuccess(self.PromiseValue);
resolve(res);
})
self.rejectedCb.push(function(err) {
// 这里返回的res有可能是promise,但是我们没有做处理
var er = handleFail(self.PromiseValue);
reject(er);
})
})
}
...

因为我们返回的是Promise,由于我们没有做处理,导致无法正确的获取到值。

...
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
resolve(res);
}
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
if (er instanceof Promise) {
er.then(resolve, reject);
} else {
reject(er);
}
})
})
}
...

如果返回的是一个Promise,就继续塞入到then里面。

在执行一下:

async1().then(function(result){
console.log('result = ' + result)
return async2();
}).then(function(result){
console.log('result = ' + result)
return async3();
}).then(function(result){
console.log('result = ' + result)
return result;
})

// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 执行async1
// => result = zqz1
// => 执行async2
// => result = zqz2
// => 执行async3
// => result = zqz3

最后一个简单完整的 then:

Promise.prototype.then = function (handleSuccess, handleFail) {
var self = this;
var PromiseStatus = this.PromiseStatus;

if(typeof handleSuccess === 'function') {
handleSuccess = handleSuccess;
} else {
handleSuccess = function (result) {}
}

if(typeof handleFail === 'function') {
handleFail = handleFail;
} else {
handleFail = function (err) {}
}

if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
resolve(er);
}
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
if (er instanceof Promise) {
er.then(resolve, reject);
} else {
reject(er);
}
})
})
}
if(PromiseStatus === 'resolved') {
return new Promise(function(resolve, reject) {
var result = handleSuccess(self.PromiseValue);
resolve(result);
})
}
if(PromiseStatus === 'rejected') {
return new Promise(function(resolve, reject) {
var result = handleFail(self.PromiseValue);
reject(result);
})
}

}

参考


  • Promise
  • implementing
  • 剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

推荐阅读
  • 在编写业务代码时,常常会遇到复杂的业务逻辑导致代码冗长混乱的情况。为了解决这个问题,可以利用中间件模式来简化代码逻辑。中间件模式可以帮助我们更好地设计架构和代码,提高代码质量。本文介绍了中间件模式的基本概念和用法。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 一、什么是闭包?有什么作用什么是闭包闭包是定义在一个函数内部的函数,它可以访问父级函数的内部变量。当一个闭包被创建时,会关联一个作用域—— ... [详细]
  • ECMA262规定typeof操作符的返回值和instanceof的使用方法
    本文介绍了ECMA262规定的typeof操作符对不同类型的变量的返回值,以及instanceof操作符的使用方法。同时还提到了在不同浏览器中对正则表达式应用typeof操作符的返回值的差异。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 006_Redis的List数据类型
    1.List类型是一个链表结构的集合,主要功能有push,pop,获取元素等。List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,List的设 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
author-avatar
我就是个2丶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有