ES6版Promise实现,给你不一样的体验

摘要: 在很久很久以前,Promise还没有来到这个世上。那时森林里的有这样一群攻城狮,他们饱受回调地狱(回调金字塔)的摧残,苦不堪言。直到有一天,一位不愿意留下姓名的特工横空出世,将他们从回调地狱中解救了出来,代号Promise。自此,很多人都踏上了寻找Promise的道路,我亦如此...

友情提醒: 本文使用ES6实现的Promise,不会的童鞋们请自行脑补!What?这位同学你竟然不知道ES6,好的,放学了请不要走,我们单独交流一下......

回调地狱 VS Promise

就拿fs(node的核心包)来说吧,假如我们需要同时请求a.txtb.txt中的数据,然后对数据进行操作。这种需求在我们的开发中也经常遇到哦!

  • 曾经的回调地狱
let fs = require('fs');
let arr = [];
fs.readFile('a.txt','utf8',function(err,data){arr.push(data);fs.readFile('b.txt','utf8',function(err,data){arr.push(data);// 如果需要把更多的文件数据,那滋味不敢想象console.log(arr); })
})
复制代码
  • 现在的Promise
let fs = require('fs');
function read(url,coding){ // 首先我们对fs.readFile()进行promise封装return new Promise((resolve,reject)=>{fs.readFile(url,coding,function(err,data){if(err) reject(err);resolve(data);})})
}
Promise.all([read('a.txt','utf8'),read('b.txt','utf8')]).then(data=>{// 这里我们就可以直接操作请求到的两个文件的数据了,Promise还很贴心的返回了一个数组console.log(data);
})
复制代码

相比较之下,Promise和回调地狱的战争起初就不是一个等级的呀,回调地狱听起来强大,但实则一点不经揍啊!Promise此时的内心应该是这样的:

Promise之自己实现

看到这里,相信大家都很想知道Promise的核心实现是什么?接下来,请小伙伴们不要闭眼,看这里,看这里!我便说说我是如何在寻找Promise的道路上一条道走到黑的。(这标题起的,笑出猪叫声)

1、Promise 类封装

起初,我发现Promise是可以被new的,说明Promise 的出身是一个类啊,这可是一条很有价值的线索啊。(大家都知道,还用你说)

class Promise {constructor(executor) { // executor是new Promise的参数this.status = 'pending'; // 保存状态this.reason = undefined; // 失败的原因this.value = undefined; // 成功的结果let resolve = (value)=> {if(this.status === 'pending'){this.status = 'resolved';this.value = value;}}let reject = (reason)=>{if(this.status === 'pending'){this.status = 'rejected';this.reason = reason;}}try {executor(resolve, reject); // 执行器       } catch (e) {reject(e);}}// 定义实例上的then方法,then调用的时候都是异步调用的 then(onFulfilled, onRejected) {if(this.status === 'resolved'){ // status状态改变时执行onFulFilledonFulfilled(this.value);}if(this.status === 'rejected'){ // status状态改变时执行onFulFilledonRejected(this.reason);}}
}
复制代码

这怎么仅仅一条线索就写出来这么东东呀,真让人摸不着头脑!别急,听我慢慢道来:

  • executor:执行器,默认是new的时候就自动执行,executor的执行是同步的,为什么要try一下呢,executor执行时如果throw new Error('error'),直接走reject
  • resolve, reject:定义了executorresolve成功的回调函数和reject失败的回调函数两个参数
  • reason,value:分别代表了成功返回的值和失败的原因
  • status:保存了Promise的三种状态pending(等待态),fulfilled(成功态),rejected(失败态)
    • 当一个promise的状态处于pending时,它可以过渡到fulfilled或者ejected
    • 当一个promise的状态处于fulfilled时或者rejected时,不能再过渡到其他任何状态
  • then函数: 因Promise是可以链式调用的,说明then函数是定义在Promise类的原型Prototype上的。

这样我们就成功处理了同步情况下的Promise,是不是觉得自己已经追寻到Promise的终极力量了呢。(抽根烟,平复下躁动的心情)

2、Promise异步的实现

在我们平时的开发中,往往异步代码比较多,异步执行需要,然而Promiseexecutor执行器又是同步执行的,它不等我们怎么办呢,好着急有木有。 我们在上面代码的基础上新增如下几行代码:

class Promise {constructor(executor) {this.onResolvedCallbacks = []; // 保存成功的回调this.onRejectedCallbacks = []; // 保存失败的回调let resolve = (value)=> {if(this.status === 'pending'){this.status = 'resolved';this.value = value;this.onResolvedCallbacks.forEach(fn=>fn());}}let reject = (reason)=>{if(this.status === 'pending'){this.status = 'rejected';this.reason = reason;this.onRejectedCallbacks.forEach(fn=>fn());}}}then(onFulfilled, onRejected) { if(this.status === 'pending'){this.onResolvedCallbacks.push(()=>{onFulfilled(this.value);});this.onRejectedCallbacks.push(()=>{onRejected(this.reason);});});}}
}
复制代码

当出现异步代码时,status的状态还是pending,我们可以先把then函数中成功和失败的回调保存下来,等到异步代码执行完成后,status的状态改变了,我们再去依次执行保存下来的回调函数。

看到这里,如果觉得自己已经基本掌握Promise的实现,只能说尔等对Promise的核心力量一无所知。(别废话,赶紧写)好的,各位大佬!

3、Promise之链式调用的实现

在开始实现之前呢,我们先来看一下如下代码:

//  这里的Promise是ES6封装好的,并不是我们自己实现的
let promise = new Promise((resolve,reject)=>{ resolve('123');
})
let promise2 = promise.then((data)=>{throw new Error('error');
})
promise2.then((data)=>{console.log(data);
},(err)=>{console.log(err); // 这里输出了error
})
复制代码

上面代码说明then函数执行后返回的promise2实例并不是promise实例,说明返回值不是this,因为promise不能即调用成功后不能再走失败,所以then函数执行后返回的promise2是一个新的promise实例。(跟jQuery的链式调用不一样哦)

Promiseconstructor的代码不需要改变,只需要对then函数进行再次封装:

then(onFulfilled, onRejected) {// onFulfilled和onRejected可能没传onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=>value;onRejected = typeof onRejected === 'function' ? onRejected : (err)=>{throw err};let promise2; // 需要每次调用then方法时,都需要返回新的promisepromise2 = new Promise((resolve, reject)=>{if(this.status === 'resolved'){setTimeout(()=>{try {let x = onFulfilled(this.value); // 执行完当前回调后返回的结果可能还是个promiseresolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)}if(this.status === 'rejected'){setTimeout(()=>{try {let x = onRejected(this.reason);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0) }if(this.status === 'pending'){this.onResolvedCallbacks.push(()=>{setTimeout(()=>{try {let x = onFulfilled(this.value);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)});this.onRejectedCallbacks.push(()=>{setTimeout(()=>{try {let x = onRejected(this.reason);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)});}})return promise2;}
复制代码
  • onFulfilled,onRejected:当没有传的时候,需要做的处理
  • promise2then函数的返回值是一个新的promise
  • setTimeout:Promise/A+规范(规范)要求then函数必须是异步的,当然原生的Promise实现并不是用的setTimeout,而是一个微任务
  • resolvePromise:封装resolvePromise方法,当then函数中的成功或者失败函数返回值x可能还是个promise

定义的resolvePromise方法:

let resolvePromise = (promise2,x,resolve, reject)=>{let called;// promise2和函数的返回值是同一个if(promise2 === x){return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));}if(x!==null && (typeof x === 'object' || typeof x === 'function')){try {let then = x.then;if(typeof then === 'function'){then.call(x,(y)=>{if(called) return;called = true;resolvePromise(promise2,y,resolve, reject);// 递归处理,直到y是一个普通值},(err)=>{if(called) return;called = true;reject(err);})}else{ // then如果是一个常量if(called) return;called = true;resolve(x);}} catch (e) {if(called) return;called = true;reject(e);}}else{ // x如果是一个常量if(called) return;called = true;resolve(x);}
}
复制代码
  • 四个参数:promise2 (then函数的返回值,是一个新的promise) x(then中成功后者失败函数的返回值)resolve(promise2的resolve)reject(promise2的reject)
  • called: 加了called判断,防止多次调用,因为这里的逻辑不单单是自己的,还有别人的,别人的promise可能即会调用成功也会调用失败
  • let then = x.thenx可能还是一个promise,那么就让这个Promise执行

至此,我们终于追寻到了promise的核心力量所在。来,让我们小小的庆贺一下:

3、Promise之类上的方法实现

当然,我们已经初步了解了promise的核心力量,在我们开发的过程中,除了then方法,也会使用它的一些其他常用的方法,就像一位身经百战的特工,你除了会用刀,还要会用枪不是。我们在Promise类上定义它们:

static resolve(value){return new Promise((resolve,reject)=>{resolve(value);})}static reject(reason){return new Promise((resolve,reject)=>{reject(reason);})}static all(promises){return new Promise((resolve,reject)=>{let arr = [];let i = 0;let processData = (index,data)=>{arr[index] = data;if(++i === promises.length){resolve(arr);}}for(let i = 0; i< promises.length; i++){promises[i].then(data=>{processData(i,data);},reject);}})}static race(promises){return new Promise((resolve,reject)=>{for(let i = 0; i< promises.length; i++){promises[i].then(resolve,reject);}})}catch(onRejected){return this.then(null,onRejected);}
复制代码

相信resolve,reject,all,race这四个类上的方法和catch这个原型的方法大家都已经很熟悉了,我就不过多的啰嗦了。

因为,我实在是编不下去了,我还有更重要的事情要去做:

结语: 花了很久写的这篇文章,如果这篇文章令你或多或少有些收获,请不要吝啬你的赞美(点个赞再走吗,小哥哥小姐姐),如果有写的不对的地方,也希望各位大佬能不吝指教,万分感谢!原创文章,转载请注明出处!

ES6版Promise实现,给你不一样的体验相关推荐

  1. 【ES6】Promise对象详解

    [ES6]Promise对象详解 一.Promise对象的含义 二.Promise对象的用法 三.Promise对象的几个应用[重点] 1.时间延迟函数 2.图片异步加载 查看更多ES6教学文章: 参 ...

  2. 手写简单版 Promise

    Promise作为ES6新增的函数,帮助我们解决了回调地狱的难题,让我们的异步代码可以更加清晰简洁,作为一名前端程序员,手写简单版Promise应该是必备的技能.接下来不多说,直接上代码了. clas ...

  3. es6的Promise(承诺)

    es6的Promise(承诺) Promise中的所有回调函数,都是异步执行的 //用来解决异步的一些问题 let p = new Promise(function(resolve,refused){ ...

  4. 利用ES6进行Promise封装总结

    原生Promise解析 简介 promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和强大. promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一 ...

  5. 浅谈ES6原生Promise

    ES6标准出炉之前,一个幽灵,回调的幽灵,游荡在JavaScript世界. 正所谓: 世界本没有回调,写的人多了,也就有了})})})})}). Promise的兴起,是因为异步方法调用中,往往会出现 ...

  6. 谈谈 ES6 的 Promise 对象

    2019独角兽企业重金招聘Python工程师标准>>> 谈谈 ES6 的 Promise 对象 异步编程 promise es6 javascript 前言 开篇首先设想一个日常开发 ...

  7. 深度解析利用ES6进行Promise封装总结

    这篇文章主要介绍了如何利用ES6进行Promise封装总结,文中通过示例代码介绍的非常详细,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. 原生P ...

  8. 用es5实现es6的promise,彻底搞懂promise的原理

    1.promise的含义 Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Pr ...

  9. 前端JS校验银行卡卡号和身份证号码(附ES6版方法)

    1.银行卡卡号校验方法. function luhnCheck(bankno) { var lastNum = bankno.substr(bankno.length - 1, 1); //取出最后一 ...

最新文章

  1. php如何编写应用程序,编写安全 PHP 应用程序的七个习惯
  2. 细述:nginx http内核模块提供的变量和解释
  3. 基于Spring框架的Controller中进行事务管理
  4. html调用rpst 源码_parseHTML 函数源码解析(四) AST 基本形成
  5. 证明3-SAT问题是NP-complete
  6. 基于kali linux 跑字典暴力破解wifi教程
  7. 2016年计算机一级excel试题及答案,2016年计算机一级试题加答案
  8. python笔记:7.2.2.2 一元多因素方差分析_交互效应(购房面积影响因素交互效应)
  9. 从广域网云化看SD-WAN
  10. 麦子学院项目-懒人天气App思维导图、素材下载
  11. python aes加密_在不到5分钟的时间内用python编码aes128位加密
  12. 【带你快速了解人工智能开发Python基础课程第二周】
  13. 微信公众号开发【一】 菜单获取与设置
  14. 【人类观察所】quot;当代人quot;正经历的生活
  15. win10无线显示未连接到服务器,Win10系统无线网络适配器显示未连接的解决方法...
  16. linux如何提取文件名称,如何在Linux中提取路径和文件名的特定部分
  17. wifi 直接序列扩频传输技术(DSSS)及1M,2M速率时调制和编码方式
  18. python接口自动化测试 ( 第一章 )
  19. windows下安装包制作软件:NSIS的使用方法
  20. 阿里巴巴上市背后的技术力量

热门文章

  1. js 字符和html和数值拼接,js中substring和substr的用法(获取字符串为整个html页面中的某个数值)...
  2. php内置函数和扩展,PHP 内置函数strlen 和mbstring扩展函数mb_strlen的区别
  3. cpci检索为什么那么慢_索引原理与慢查询优化
  4. xgboost与coo_matrix
  5. L1标准化与L2标准化
  6. python怎么输出小数部分_python 输出小数控制
  7. c语言获取当前日期和时间
  8. python好不好用_但python以前不太好用
  9. 笔记-高项案例题-2017年上-质量管理
  10. Leaflet中使用layerGroup图层组实现图层切换