写在前面
这个文章,展现的是一个实现Promise的思路,以及如何发现和处理问题的情境。
从现有的Promise分析
如果我们想要自己实现一个简单的Promise
,那现有规范规定的Promise
肯定是我们最好的参照。
我们先看下Promise
怎么使用:
var promise1 = new Promise(function(resolve, reject){
// 成功后的TODO
resolve(value);
// 失败后的TODO
reject(err);
})
来看下返回的promise1
是什么,以及它的结构是怎么样的:
再进行一些具体操作
var promise1 = new Promise(function(resolve, reject) {
resolve('zqz')
})
promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})
// => 'zqz'
var promise1 = new Promise(function(resolve, reject) {
reject('出现异常')
})
promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})
// => '出现异常'
从Promise的 使用方式上 和 实例 可以看到哪些东西:
- Promise是一个构造函数
- Promise包含一个参数,这个参数类型是一个_匿名函数_
- 匿名函数包括2个形参,分别是
reject
和resolve
- 这两个形参类型是 函数 ,且
reject
和resolve
都有一个参数, 参数类型不限定 - 实例 是个 Promise
- 实例的 原型 上挂载了 2个方法,分别是
then
和catch
,同时then可以有多个,所以需要一个回掉函数队列 - 实例上 有2个属性,分别是
PromiseStatus
和PromiseValue
- Promise根据定义 PromiseStatus 需要有 3种状态,分别是
pending
,fulfilled
,rejected
根据上面的分析情况,我们先简单的来构造一个雏形。
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)
}
}
当然这还不够,因为重要的两个功能then
和catch
还没有实现。
从现有的 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等)的时候,这时候
PromiseStatus
是pending
状态
在来看下我们在pending
时候的处理
...
// 异步时
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
// 这里只是将函数塞入队列,然后就没有然后来。。。这是有问题的
self.resolvedCb.push(handleSuccess);
self.rejectedCb.push(handleFail);
})
}
...
这时候我们的两个数组:resolvedCb
和rejectedCb
就发挥作用了,由于我们不知道异步什么时候结束,但是我们可以根据他们定义的先后顺序注入到 队列
中,然后根据 顺序
依次执行,这样也就保证了异步操作的执行顺序。
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原理与实现探究的一种思路的更多相关文章
-
promise原理
简介 Promise 对象用于延迟(deferred) 计算和异步(asynchronous )计算.一个Promise对象代表着一个还未完成,但预期将来会完成的操作.Promise 对象是一个返 ...
-
这一次,彻底弄懂 Promise 原理
作者声明 本人将迁移至个人公众号「前端Q」及「掘金」平台写文章.博客园的文章将不再及时更新发布.欢迎大家关注公众号「前端Q」及我的掘金主页:https://juejin.im/user/5874526 ...
-
Promise原理 &;&; 简单实现
Promise原理 参考https://github.com/chunpu/promise/blob/master/promise.js 个人认为原博的实现有点问题 在next函数的实现上, 会导致无 ...
-
Promise 原理
异步:可同时好几件事,互不影响: 同步:按循序一件一件.... 异步好多缺点:.... promise就是解决异步计算的这些缺点的,主要用于: 1.异步计算: 2.可以将异步操作队列化 按期望的顺序 ...
-
解决Geoserver请求跨域的几种思路
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景描述 跨域问题是浏览器同源安全制引起的特别常见的问题.不同前端语 ...
-
解决Geoserver请求跨域的几种思路,第二种思路用过
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景描述 跨域问题是浏览器同源安全制引起的特别常见的问题.不同前端语 ...
-
Func-Chain.js 另一种思路的javascript异步编程解决方案
本文转载自:https://www.ctolib.com/panruiplay-func-chain.html Func-Chain.js 另一种思路的javascript异步编程,用于解决老式的回调 ...
-
js数组去重几种思路
在一些后台语言中都内置了一些方法来处理数组或集合中重复的数据.但是js中并没有类似的方法,网上已经有一些方法,但是不够详细.部分代码来源于网络.个人总计如下:大致有4种思路 1)使用两次循环比较原始的 ...
-
CSS实现水平垂直同时居中的5种思路
× 目录 [1]水平对齐+行高 [2]水平+垂直对齐 [3]margin+垂直对齐[4]absolute[5]flex 前面的话 水平居中和垂直居中已经单独介绍过,本文将介绍水平垂直同时居中的5种思路 ...
随机推荐
-
asp.net core 使用EF7 Code First 创建数据库,同时使用命令创建数据库
1.首先下载vs2015的Asp.Net Core(RC2)的插件工具(https://www.microsoft.com/net/core#windows)2.创建一个asp.net Core的项目 ...
-
.net单元测试初探
写在前面 组里接手了一个在运行的票台系统,包括收银,客户体验,店内商超等子系统,要求将服务端进行云端化,以应对分店的增多和决策层对于数据的需要,而随着时间的退役和各种收费策略的改变,促销活动的展开等, ...
-
Linux下Redis服务器安装配置
说明:操作系统:CentOS1.安装编译工具yum install wget make gcc gcc-c++ zlib-devel openssl openssl-devel pcre-devel ...
-
SAP采购申请审批记录增强
业务需要,开发就搞.... EBAN中增强结构:CI_EBANDB ANAME 类型 UNAME 用户名 ADATE 类型 AEDAT DATS 更改日期 ATIME 类型 UZEIT TIMS 时间 ...
-
[SEO] 网站标题分隔符
标题用什么分隔符对SEO最有利 我们在看同行的朋友对网站标题优化时,关键词分按照主次的顺序进行分布,在网站标题或者是关键词之间都会有一个符号,俗话来讲就称为关键词分隔符,网站标 题分隔符分为“-”(横 ...
-
程序猿的编程神器 - vim
一.官方文档: 当你首次安装好 Vim 之后.能够用 :help tutor 或者 :help tutor@cn 命令.进入一个30分钟的 Vim 新手教程.你也能够下载一个 Vim Document ...
-
zTree异步生成数据时无法获取到子节点的选中状态
最近在项目中遇到一个问题,需求如下: 根据选中不同的人员(ID)向后台发送ajax请求,通过返回的数据来生成该人员的权限访问树,该树目录最少为3级目录,在生成的时候会自动勾选上次保存过的选中状态,点击 ...
-
objective-C学习笔记(六)继承与多态
封装 encapsulation 隐藏对象内部实现细节,对外仅提供公共接口访问. (说白了就是属性啊,方法啊全都写在类内,对外只提供访问,不需要了解细节) 继承 inheritance 一个类 ...
-
Sublime 常用快捷键
Ctrl+Shift+P:打开命令面板 Ctrl+P:搜索项目中的文件 Ctrl+G:跳转到第几行 Ctrl+W:关闭当前打开文件 Ctrl+Shift+W:关闭所有打开文件 Ctrl+Shift+V ...
-
使用Python从Workflowy同步大纲到印象笔记
title: 从Workflowy到印象笔记 toc: true comment: true date: 2018-03-17 10:05:54 tags: ["Python", ...