手写一款符合Promise/A+规范的Promise
手写一款符合Promise/A+规范的Promise
长篇预警!有点长,可以选择性观看。如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心
Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难
本篇大概分为以下步骤
- 实现简单的同步Promise
- 增加异步功能
- 增加链式调用then
- 增加catch finally方法
- 增加all race 等方法
- 实现一个promise的延迟对象defer
- 最终测试
实现简单的同步Promise
先大概说一下基本概念:
Promise内部维护着三种状态,即pending,resolved和rejected。初始状态是pending,状态可以有pending--->relolved,或者pending--->rejected.不能从resolve转换为rejected 或者从rejected转换成resolved.
即 只要Promise由pending状态转换为其他状态后,状态就不可变更。
ok.知道了这些后,我们开始手撸代码:
注意观看序号 1 2 3 4 5 ...
function Promise(executor){
let that = this;
/** 2 定义初始的一些变量 */
that.status = 'pending';
that.value = null;
that.reason = null;
/** 3 定义初始的成功和失败函数 */
function resolve(value){
/** 4 判断状态是不是初始状态pending
* 是就转换状态 否则不转换
* 确保状态的变化后的不可变性 */
if(that.status === 'pending'){
that.status = 'resolved';
that.value = value;
}
}
function reject(reason){
/** 5 同上 */
if(that.status === 'pending'){
that.status = 'rejected';
that.reason = reason;
}
}
/**
* 1 Promise中首先传了一个executor,它是一个函数
* executor函数中又传了两个函数,分别是resolve和reject
* 很显然 resolve是成功回调,reject是失败的回调
*/
executor(resolve,reject);
}
/** 6 在Promise原型上面定义then方法
* then方法上面有两个回调 一个是成功后的方法 另一个是失败后的方法
* 根据成功或失败的状态去执行相关成功onFilfulled()或者失败onRejected()的回调方法
*/
Promise.prototype.then = function(onFilfulled,onRejected){
let that = this;
if(that.status === 'resolved'){
/** 7 如果状态已经变更为resolved
* 说明resolve方法已经被调用
* 那么此时就执行成功的回调函数onFilfulled
* 并传入参数 that.value
* */
onFilfulled(that.value);
}
if(that.status === 'rejected'){
/** 8 同上
* 传入参数 that.reason
*/
onRejected(that.reason);
}
}
module.exports = Promise;
通过require()引入手撸的Promise
let Promise = require('./myPromise');
let p1 = ()=>{
return new Promise((resolve,reject)=>{
resolve('success.1');
});
}
p1().then((data)=>{
console.log(data); // 打印 success.1
},(err)=>{
console.log(err);
});
ok.经调用发现 此代码可以实现部分Promise的功能,但仅仅是同步下才有效果。
那异步呢? 别急这就来~:
增加异步功能
注意观看序号 1 2 3 4 5 ...
function Promise(executor){
let that = this;
that.status = 'pending';
that.value = null;
that.reason = null;
/** 1 因为异步不是立即执行 状态不会变更 成功或失败的回调函数也不会执行
* 所以先定义好存放成功或失败回调函数的数组
* 以便将成功或失败的回调函数先保存起来
* */
that.onFilFulledCallbacks = [];
that.onRejectedCallbacks = [];
function resolve(value){
if(that.status === 'pending'){
that.status = 'resolved';
that.value = value;
/** 3 发布
* 等待状态发生变更
* 状态变更后 立即执行之前存放在相应数组中所有的成功或失败的回调函数
* 即 发布
*/
that.onFilFulledCallbacks.forEach((fn)=>{
fn();
});
}
}
function reject(reason){
if(that.status === 'pending'){
that.status = 'rejected';
that.reason = reason;
/** 4 同上 */
that.onRejectedCallbacks.forEach((fn)=>{
fn();
});
}
}
executor(resolve,reject);
}
Promise.prototype.then = function(onFilfulled,onRejected){
let that = this;
if(that.status === 'resolved'){
onFilfulled(that.value);
}
if(that.status === 'rejected'){
onRejected(that.reason);
}
/** 2 订阅
* 因为是异步 状态当时并没有立即变更 所以状态还是pending
* 此时需要把成功或者失败的回调函数存放到对应的数组中
* 等待状态变更时 再从数组中拿出来去执行
* 即 订阅
* *存放数组时 为了执行时方便 需要把回调函数的外层包裹一层空函数
*/
if(that.status === 'pending'){
that.onFilFulledCallbacks.push(function(){
onFilfulled(that.value);
});
}
if(that.status === 'pending'){
that.onRejectedCallbacks.push(function(){
onRejected(that.reason);
});
}
}
module.exports = Promise;
代码测试:
let Promise = require('./myPromise');
let p1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('success.1');
// reject('fail.');
},1500);
});
}
p1().then((data)=>{
console.log(data); // success.1
},(err)=>{
console.log(err);
});
可以看到 1.5s后 执行了resolve() 并打印了success.1,至此,我们实现了异步的Promise.其实这里的实现异步的思想就是发布订阅.
en~ok.高能预警
手写一款符合Promise/A+规范的Promise的更多相关文章
- 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise
本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...
- 如何手写一款KOA的中间件来实现断点续传
本文实现的断点续传只是我对断点续传的一个理解.其中有很多不完善的地方,仅仅是记录了一个我对断点续传一个实现过程.大家应该也会发现我用的都是一些H5的api,老得浏览器不会支持,以及我并未将跨域考虑入内 ...
- 一步一步实现一个Promise A+规范的 Promise
2015年6月,ES2015(即ES6)正式发布后受到了非常多的关注.其中很重要的一点是 Promise 被列为了正式规范. 在此之前很多库都对异步编程/回调地狱实现了类 Promise 的应对方案, ...
- 如何手写一款SQL injection tool?
0×01 前言 我想在FreeBuf上出没的人一般都是安全行业的,或者说是安全方面的爱好者,所以大家对sql注入应该都比较了解,反正我刚入门的时候就是学的这些:sql注入.xss之类的.sql注入从出 ...
- [手写系列] 带你实现一个简单的Promise
简介 学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的 本次将带小伙伴们实现Promise的基本功能 Promise的基本骨架 Promise的then Pr ...
- 手写符合Promise/A+规范的Promise
const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "r ...
- 手写基于Promise A+规范的Promise
const PENDING = 'pending';//初始态const FULFILLED = 'fulfilled';//初始态const REJECTED = 'rejected';//初始态f ...
- 参考KOA,5步手写一款粗糙的web框架
我经常在网上看到类似于KOA VS express的文章,大家都在讨论哪一个好,哪一个更好.作为小白,我真心看不出他两who更胜一筹.我只知道,我只会跟着官方文档的start做一个DEMO,然后我就会 ...
- 手写一个Promise/A+,完美通过官方872个测试用例
前段时间我用两篇文章深入讲解了异步的概念和Event Loop的底层原理,然后还讲了一种自己实现异步的发布订阅模式: setTimeout和setImmediate到底谁先执行,本文让你彻底理解Eve ...
随机推荐
- c#基础 第三讲
Random r = new Random(); string x, y; while (true) { ...
- 转载 Mixed Content Page
网站配置了https之后,网页上的百度地图无法正常显示,报错类似于: Mixed Content: The page at 'https://url_1' was loaded over HTTPS, ...
- win7卸载IE11
好多人升级了IE11后发现各种不好用,比如经常卡死,无响应.调试工具不好用等缺点. 现在告诉你如何卸载IE11 查看已安装的更新 右键wie卸载,即可需要重启
- 各大网站CSS初始化代码集合
css代码之所以初始化,是因为能尽量减少 各浏览器之间的兼容性问题! 腾讯QQ官网 样式初始化 body,ol,ul,h1,h2,h3,h4,h5,h6,p,th,td,dl,dd,form,fiel ...
- 任务调度quartz整理
一张图,了解quartz运行机制: 此图表示:Scheduler是容器,Trigger是多个触发器,jobDetail是多个任务,Calendar是多个日历. jobDetail任务,需要指定类实现J ...
- sevlet实现反盗链
有时候为了网站的版权和安全问题,我们需要为我们的网站应用设置防盗链,这样可以保证我们网站的一些资源的安全性.防盗链的主要是通过获取http的请求头referer的信息来和我们的网站地址做对比,如果相同 ...
- HDU4686—Arc of Dream
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4686 题目意思:给出一个n,算这个式子,给出A0,B0,AX,AY,然后存在以下的递推关系. a0 = ...
- golang-imagick图片去除边框(黑边,白边,),添加中文字
图片去除边框(黑边,白边) package main import ( "fmt" "github.com/gographics/imagick/imagick" ...
- Python慢,为啥还有大公司用?
PyCon 是全世界最大的以 Python 编程语言 为主题的技术大会,大会由 Python 社区组织,每年举办一次.在 Python 2017 上,Instagram 的工程师们带来了一个有关 Py ...
- WebRtc(网页即时通讯技术)知识点总结
前言 WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,简而言之它是一个支持网页浏览器进行实时语音对话或视频对话的技术.并且还支持跨平台:window ...