本文为饥人谷讲师若愚原创文章,首发于 前端学习指南。

问题

假设我们有一个需求:1. 获取用户所在的城市;2. 根据城市获取天气;3. 根据天气获取出行建议。那我们的代码应该是这样的

getCity(url1, function(){getWeather(url2, function(weather){getSuggestion(url3, function(suggestion){console.log(suggestion)})})
})

这就是典型的异步 callback 『回调地狱』,代码层层嵌套可读性很差。关于异步的解决方式可参考这篇文章 Node.js异步漫谈

使用 Promise 是解决上述问题的一种方式,这里我们不去讲如何去使用内置的 Promise,而是带大家手把手写一个 Promise。

思路

我们希望有一个工具,能让我们使用下面的的写法来实现上述功能

promise.then(getCity).then(getWeather).then(getSuggestion)

整理下思路:

  1. Tool 是一个对象
  2. Tool 有 then 这个方法
  3. 执行 then 方法返回的应该还是 Tool 对象
function Promise(){}
Promise.prototype.then = function(fn){//todo...return this
}
var promise = new Promise()

那如何实现异步操作序列执行呢?关键思路如下:

在 promise 对象内容维护一个数组,当执行 promise.then(getCity) .then(getWeather) .then(getSuggestion) 时把这几个函数依次放入数组中。注意此时这些函数并没有执行。

执行promise.resolve()时,会从数组中拿出一个函数去执行。函数执行的过程中在异步操作的结果到来后会再次自动调用 promise.resolve(),触发下一个函数的取出并执行,下一个函数结果到来后再次自动调用promise.resolve() ......,这样就实现了异步链式执行。和原子弹爆炸原理类似。

所以需要对原来的异步函数做一点小小的改动,在数据到来的地方,加一个promise.resolve,用于启动后续函数的执行

function getCity(){var xhr = new XMLHttpRequest()xhr.open(url, 'get', true)xhr.onload = function(){if (this.status == 200) {promise.resolve(xhr.responseText)  //注意这里的promise.resolve}}xhr.send()
}

现在我们就能实现一个简易的 Promise 了,这里我们先暂不考虑特殊情况:

function Promise(){this.callbacks = []
}
Promise.prototype.then = function(fn){this.callbacks.push(fn)  //调用 then 时把函数放入数组return this              //返回当前对象供链式调用
}
Promise.prototype.resolve = function(data){var fn = this.callbacks.shift()  //当调用resolve时拿出一个函数fn&&fn(data)                     //执行这个函数,并且把resolve的参数做参数
}var promise = new Promise()promise.then(getCity).then(getWeather).then(getSuggestion)promise.resolve()  //启动function getCity(){setTimeout(function(){promise.resolve('杭州')}, 1000)
}
function getWeather(city){setTimeout(function(){promise.resolve(city + ' 晴天')}, 1000)
}
function getSuggestion(weather){setTimeout(function(){console.log(weather + ' 天气不错,可携女友与狗出行')}, 1000)
}

当然,如果觉得promise.resolve 单独启动一次看起来不舒服,也可以这样执行

getCity().then(getWeather).then(getSuggestion)function getCity(){setTimeout(function(){promise.resolve('杭州')}, 1000)return promise   //注意这里
}

实现

到此为止我们已经写了一个简单的 Promise,甚至能满足很大一部分使用需求。但有个问题,每次异步操作可能存在失败的情况,而上面的代码并没有异步函数的失败处理。下面考虑异步的失败处理,原理和上面类似,可以阅读代码动手做个测试

class Promise {constructor (){this.callbacks = []this.oncatch = null}reject(result){this.complete('reject', result)}resolve(result){this.complete('resolve', result)}complete(type, result){if(type==='reject' && this.oncatch){this.callbacks = []this.oncatch(result)}else if(this.callbacks[0]) { var handlerObj = this.callbacks.shift()if(handlerObj[type]){handlerObj[type](result)}}}then(onsuccess, onfail){this.callbacks.push({resolve: onsuccess,reject: onfail})return this}catch(onfail){this.oncatch = onfailreturn this}}var promise = new Promise()fn1().then(fn2, onfn1error).then(fn3, onfn2error).catch(onerror)function fn1(){setTimeout(function(){if(Math.random()>0.5){promise.resolve('杭州')}else{promise.reject('fn1 error')}})return promise}

总结

现在我们已经手写了一个 Promise, 当然和浏览器内置对象Promise原理有些差异, 但至少『达到』类似的目的了

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

十行代码实现高仿Promise相关推荐

  1. java使用微信表情代码_iOS高仿微信表情输入功能代码分享

    最近项目需求,要实现一个类似微信的的表情输入,于是把微信的表情扒拉出来,实现了一把.可以从这里下载源码.看起来表情输入没有多少东西,不外乎就是用NSTextAttachment来实现图文混排,结果在实 ...

  2. 短链接java代码_java高仿新浪微博短链接地址生成工具ShortUrlGenerator.java

    仿新浪微博 短链接地址生成工具 ShortUrlGenerator.java String sLongUrl = "http://tech.sina.com.cn/i/2011-03-23/ ...

  3. html5支付宝主页面代码,JavaScript高仿支付宝倒计时页面及代码实现

    实现目标 一,页面在图一时开始进行倒计时(可以点击取消订单按钮,支付页面消失). 二,倒计时完毕,出现删除订单. 三,单击删除订单,弹出弹框,询问是否要真正删除订单. 四,单击确定,即可删除订单. 如 ...

  4. 飞机大战代码(高仿),完美复原原游戏,好玩到停不下来

    分数展示 void Game::printScore() {if(score == 120 && flag_rank == 0){rank -= 3;flag_rank = 1;}el ...

  5. 高仿QQ电脑管家8 界面

    去年发了高仿QQ2012登录界面,最近又优化了下代码,先看效果图 换肤 的代码和高仿QQ2012登录界面一样,代码请看那边; 这次主要是优化了控件的组合,和贴图方式 整体的框架: 首先是一个窗口,这里 ...

  6. github,源码,高仿 直播

    仿花田:相亲网站 意中人 已在GitHub上开源 从头开始写一款开源app上线,相互学习 SlideMenuControllerSwift swift实现双侧边栏菜单 StepView github ...

  7. Android 高仿App项目归纳整理,持续更新中…

    Android 高仿App项目归纳整理,持续更新中- Android高仿App项目整理,包含高仿了一些大公司的app,有基于Java,Kotlin,Flutter等语言的.对于开发我们自已的项目时可以 ...

  8. 如何使用MFC编写自定义UI界面【附高仿QQ 2014登陆界面范例程序】

    地址: http://blog.csdn.net/hujkay作者:Jekkay Hu(34538980@qq.com)关键词:MFC, 编写异行窗体,自定义UI控件,VC++,异形控件,高仿QQ登陆 ...

  9. vue 判断同一数组内的值是否一直_前端代码+后端API,值得一学的Vue高仿音乐播放器实战项目

    项目名称:vue-fds_music 项目作者:符道胜 开源许可协议:Apache-2.0 项目地址:https://gitee.com/fudaosheng/vue-fds_music 项目简介 V ...

最新文章

  1. seaborn使用FacetGrid函数可视化山脊图(Ridgeline Plot with Seaborn)
  2. .NET Core 使用 nlog 进行日志记录
  3. ORACLE:RETURNING 子句
  4. python类库31[文件和目录os+os.path+shutil]
  5. 再度吐槽,PHP在centos7的安装方式稍不注意可能就打击你的积极性
  6. C++:gloox库进行协议扩展(使用自定义标签)
  7. cahrt框架 ios_iOS - Charts(一) - BarChartView
  8. Oracle全备增量备份脚本,ORACLE-RMAN:备份脚本(全库,增量)
  9. Cocos2d-x特殊节点对象(瓦片地图、粒子系统、视差滚动)概述
  10. mariadb 集群mysql_负载均衡的mariadb集群搭建
  11. 小米r1d安装php,小米路由器 一键安装LLM教程
  12. AGV机器人RFID传感器CK-G06A与西门子1200PLC应用手册
  13. 阿里云弹性计算研发团队如何从0到1自建SRE体系
  14. linux网络Netfilter与iptables技术
  15. 在线ps,修改证件照背景色
  16. Flutter 开发——识别iOS设备
  17. 牛逼的架构 vs ‘牛逼’ 的人生
  18. 在百度地图中叠加CAD图及GIS数据展示踩坑记
  19. 模仿斗地主玩法实现扑克牌的分发
  20. chrome浏览器安装插件显示程序包无效

热门文章

  1. android布局参照示例_约束布局Android示例–第2部分
  2. java面试宝典pdf下载_Java面试问答PDF电子书免费下载(250+问题,60页)
  3. 如何在Windows上安装Maven
  4. 深入了解人工智能专业怎么样
  5. 大数据的普及催生医疗信息技术市场蓝海
  6. Cloudera Hadoop 4 实战课程(Hadoop 2.0、集群界面化管理、电商在线查询+日志离线分析)...
  7. 日志服务(原SLS)新功能发布(5)--使用Logstash接入数据
  8. 查询软件和硬件列表清单[将文章里代码另存为 list.vbs,双击运行就会出现一个html页面]...
  9. live555+ffmpeg如何提取关键帧(I帧,P帧,B帧)
  10. 2012-13学年上半学期路由与交换课程设计-作业-2