Promise的封装
要封装Promise,首先要了解Promise的使用。
Promise有以下几个特点:1、Promise是一个构造函数 2、实例化Promise时有两个回调函数,resolve,reject ,成功执行resolve,失败执行reject
3、在实例化p的then中有两个对应的回调函数,第一个回调是resolve执行时触发,第二个回调是reject执行时触发4、语法糖catch,就是执行reject的时候,不调用第二个参数,直接链式写catch,像用then一样【 有坑,异步调用resolve,reject不会触发 】5、p.then()是Promise类型,p.catch()是Promise类型
先来个简单的使用实例:
// 使用resolve
let p = new Promise((resolve,reject)=>{
setTimeout(()=> {
console.log('延时器代码执行')
// 执行成功
resolve('执行成功')
},1000)
})
p.then(res=>{
console.log(res) //执行成功
})
// 使用reject
let p = new Promise((resolve,reject)=>{
setTimeout((res)=> {
// 模拟执行出错
if(!res){
// 没有给res传参
return reject('执行出错,没有参数')
}
console.log('延时器代码执行')
},1000)
})
p.then(res => {
console.log(res) //执行成功
},error => {
console.log(error) //'执行出错,没有参数' 执行出错执行
})
封装同步的Promise
function myPromise(callback) { //callback 代表( resolve,reject ) => {}
// 默认状态pending,首先没有执行成功或失败函数之前状态是pending
(this.promiseStatus = 'pending'), this.msg;
// 用that存当前this的内容
let that = this
// new 自动执行 , 调用callback
callback(
// 定义resolve
function() {
// 这里面的arguments,this都是坑,他们两个都是坑人大咖,两个都是不定值
that.promiseStatus = 'resolved';
// resolve 的第一个实参
that.msg = arguments[0];
},
// 定义reject
function() {
that.promiseStatus = 'rejected';
// rejected 的第一个实参
that.msg = arguments[0];
}
);
}
// 往myPromise 原型上添加then方法
myPromise.prototype.then = function() {
// 这里的this就是让实例调用,指向实例的,并不是指向构造函数!!! 花式秀this
if (this.promiseStatus == 'resolved') {
arguments[0](this.msg);
} else if (this.promiseStatus == 'rejected') {
arguments[1](this.msg);
}
};
// 实例化
const p = new myPromise((resolve, reject) => {
reject('失败')
}); // 使用then
p.then(
function (res) {
console.log(res);
},
function (err) {
console.log(err);
}
);
代码的思路我会尽量写详细写,以及一些容易出错的地方,想了几个小时才发现。。。。。。(无地自容)
arguments和this值得分析分析。首先是arguments,一开始想省事直接谢了()=>{}代替function(){},乖乖,好了,接下来的1个小时就是找bug时间,()=>{ this.promiseStatus;this.msg = arguments[0] } 请大家尽情笑话我,当时没有意识到箭头函数的this是指向上层的this,且不变的,由于arguments和this一样有不确定性,所以也是指向上层,所以一直纠结为什么arguments不是箭头函数()=>{ this.promiseStatus;this.msg = arguments[0] } 中 ()里的实参,而是一直是callback ,所以还是老老实实换成function(){this.promiseStatus;this.msg = arguments[0]}
this引出的2个小时的挠头疯狂:刚开始并没有发现是this的问题,而是封装完了,运行的时候,不管我在实例里调用resolve还是reject都没有打印效果,p.then()调用也没问题,然后找了then方法的定义位置,没错啊,上面callback定义的两个resolve,reject实参函数体我也各自修改了状态,单词也没写错,1个多小时过后我终于想看看是不是状态没改变,导致then定义方法里的if没执行,果然不管在实例的时候调用resolve还是reject,状态都是pending,没变过,没道理啊,我眼花了?我继续往上找,我忽然想起了刚才的argument,忽然面色暗淡,用颤抖的双手打印了function(){}里的this,呵,window。也就是根本就没变过实例里的promiseStatus属性,所以用that存下当前的this,值得一提的是用箭头函数this就不用that存了,原因在上一段,但是arguments必须又要function(){ }的形式......
异步执行resolve,reject
function myPromise(callback) { //callback 代表( resolve,reject ) => {}
// 默认状态pending,首先没有执行成功或失败函数之前状态是pending
this.promiseStatus = 'pending',
this.status = [], //异步时起作用
this.msg;
// 用that存当前this的内容
let that = this
// new 自动执行 , 调用callback
callback(
// 定义resolve
function() {
that.promiseStatus = 'resolved';
// resolve 的第一个实参
that.msg = arguments[0];
//判断存不存在resolve方法 { }
for (const {resolve} of that.status) {
resolve(that.msg)
}
},
// 定义reject
function() {
that.promiseStatus = 'rejected';
// rejected 的第一个实参
that.msg = arguments[0];
for (const {reject} of that.status) {
reject(that.msg)
}
}
);
}
// 往myPromise 原型上添加then方法
myPromise.prototype.then = function() {
// 这里的this就是让实例调用,指向实例的,并不是指向构造函数!!! 花式秀this
if (this.promiseStatus == 'resolved') {
arguments[0](this.msg);
console.log('1234'); } else if (this.promiseStatus == 'rejected') {
arguments[1](this.msg);
}else{
this.status.push({ resolve:arguments[0],reject:arguments[1] }) //实例参数有异步语句的会先执行then
console.log('yyy');
}
};
// 实例化 //对于实例化的异步我个人理解为new的过程是这样的,首先打印123是同步的setTimeout本身是同步的,里面的回调是异步的,是异步就给我等等,先执行then(),由于是自己封装的then()
是以原型方法存在,同步调用,调用以后status就变成了[{ resolve:arguments[0],reject:arguments[1] }],然后回过头执行setTimeout里的reject,这时候status有了reject
a 所以可以调用then里面的两个回调也就拿到参数:这里我想了好久。终于想明白了 同步then里面两个回调是在then中执行,异步是在定义构造函数的callback中的第一个或第二个参数定义中执行。
//所以在构造函数callback()已经完成,此时还没运行异步的reject或reject,因为只是声明函数未执行,当调用
const p = new myPromise((resolve, reject) => {
setTimeout(reject,100,'调用失败')
console.log(123)
});
// 使用then
p.then(
function (res) {
console.log(res);
},
function (err) {
console.log(err);
}
);
有一个问题想了很久,在异步执行resove或reject的时候,setTimeout( )会不会执行?是先执行setTimeout( )再执行then还是先执行then,回过头来执行setTimeout,事实上是先执行then的。紧接着then调用了一次,而那一次由于异步调用所以运行this.status.push({ resolve:arguments[0],reject:arguments[1] }),然后执行异步的reject没错。但是这时候还要执行一次then吗?不执行的话,then里面两个参数没法调用,只是声明,执行的话就执行两次then有点说不过去
因为问题想了很久,后来才发现很简单。所以要回顾一下
这里主要说的是异步执行resolve,reject,同步和上面没区别,也用不到status。
其实把还是在执行顺序的问题上。首先执行new的操作,直接进入构造函数执行一遍,接着看到了异步操作和同步操作,先执行同步的打印123没话讲,异步操作呆一边去先。往下走执行then( )然后这一步因为new中异步执行reject(以代码来说),所以status就有了一个[{reject:arguments[1]}],此时再去执行new中的reject函数就会执行(不要误以为执行setTimeout,是里面的回调是异步的)callback对应的第二个参数(函数体),在第二个函数体中for (const {reject} of that.status) {reject(that.msg)} 给then里的第二个参数(也是函数体)传了实参,所以才能打印出来。
归根结底 同步执行then的两个函数参数是在原型中的then方法定义中。
异步执行then的两个函数参数是在构造函数callback的两个函数参数中。
Promise的封装的更多相关文章
- 【转】Unity中的协同程序-使用Promise进行封装(三)
原文:http://gad.qq.com/program/translateview/7170967 译者:崔国军(飞扬971) 审校:王磊(未来的未来) 在这个系列的最后一部分文章,我们要通过 ...
- 【转】Unity中的协同程序-使用Promise进行封装(二)
原文:http://gad.qq.com/program/translateview/7170970 译者:王磊(未来的未来) 审校:崔国军(飞扬971) 在上一篇文章中,我们的注意力主要是 ...
- 使用promise手动封装ajax函数
最近在做一个单页应用,node和浏览器仅通过json传输数据,因为是只有自己用等于是锻炼一下自己,所以也不用考虑seo的问题,node端我已经写好了,但是浏览器端想要用ajax原生太麻烦,用封装的函数 ...
- 小程序 请求Promise简单封装
最近做小程序在调用后台接口的时候感觉总写很长一串,很冗杂.非常想念vue中promise封装的写法,于是自己初步封装了一下. 1.url 接口地址 2.headers请求头 3. params 请求参 ...
- 【转】Unity中的协同程序-使用Promise进行封装(一)
原文:http://gad.qq.com/program/translateview/7170767 译者:陈敬凤(nunu) 审校:王磊(未来的未来) 每个Unity的开发者应该都对协同程序非 ...
- 使用promise对象封装一个ajaxGet函数
function promiseAjax(url,data){ var pro = new Promise(function(success,failed){ 承诺一 ...
- 用Promise对象封装JQuery的AJAX过程
let jqPostAjaxPromise = function(param){ return new Promise(function(resolve, reject){ $.ajax({ url: ...
- Node.js用ES6原生Promise对异步函数进行封装
Promise的概念 Promise 对象用于异步(asynchronous)计算..一个Promise对象代表着一个还未完成,但预期将来会完成的操作. Promise的几种状态: pending:初 ...
- React Native 网络请求封装:使用Promise封装fetch请求
最近公司使用React作为前端框架,使用了异步请求访问,这里做下总结: React Native中虽然也内置了XMLHttpRequest 网络请求API(也就是俗称的ajax),但XMLHttpRe ...
随机推荐
- Duplicate a whole line in Vim
yy or Y to copy the line or dd to delete (cutting) the line then p to paste the copied or deleted te ...
- PHP学习(类型转化)
PHP 在变量定义中不需要(或不支持)明确的类型定义:变量类型是根据使用该变量的上下文所决定的.也就是说,如果把一个 string 值赋给变量 $var , $var 就成了一个 string .如果 ...
- oracle-ORA-01650错误
Unable to extend rollback segment 原因:没有足够的撤销空间用来处理所有活动事务
- bzoj1834 网络扩容
Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用.求: 1. 在不扩容的情况下,1到N的最大流: 2. 将1到N的最大流增加K所需的 ...
- 国外最受欢迎的十大社交APP网站
国外最受欢迎的十大社交APP网站 2016-11-01 09:34悠悠国外网 有哪些好的国外社交软件你知道吗,想使用国外流行的社交应用来体验不一样的社交么,想和外国友人交朋友么.本期悠悠国外网 ...
- Oracle基础知识点——Oracle常用权限理解:SYSDBA、SYSOPER、Normal、dba、connect、resource
权限介绍 系统权限 含义:系统规定用户使用数据库的权限,系统权限是针对用户对数据库的操作而言(登录数据库:读取数据表.视图:删除数据库).它只是概念上的role,只是一种登录认证时的身份标识而已. S ...
- 【C++】去除vector里重复元素的方法比较
背景:构造一个无重复的白名单,之后要在里面进行二分查找.故要求名单有序,且无重复,并且要进行二分查找,所以要采用有:随机访问迭代器类型的容器.这类容器有vector,array,deque.显然要ve ...
- GIAC2019 演讲精选 | 面向未来的黑科技——UI2CODE闲鱼基于图片生成跨端代码
一直以来, 如何从‘视觉稿’精确的还原出 对应的UI侧代码 一直是端侧开发同学工作里消耗比较大的部分,一方面这部分的工作 比较确定缺少技术深度,另一方面视觉设计师也需要投入大量的走查时间,有大量无谓的 ...
- SDUT-3331_数据结构实验之链表八:Farey序列
数据结构实验之链表八:Farey序列 Time Limit: 10 ms Memory Limit: 600 KiB Problem Description Farey序列是一个这样的序列:其第一级序 ...
- L05 Laravel 教程 - 电商实战
https://laravel-china.org/courses/laravel-shop https://laravel-china.org/topics/13206/laravel-shop-c ...