博客
关于我
手写Promise
阅读量:670 次
发布时间:2019-03-16

本文共 5507 字,大约阅读时间需要 18 分钟。

html标签中的div、head等部分可以删除,只保留核心内容

以下优化后的文章:

代码实现

function _Promise(fn) {
this.status = "pending";
this.data = null;
const _this = this;
this.cacheFn = [];
this.errorObj = { fn: null };
function resolve(data) {
_this.status = "resolved";
_this.data = data;
if (_this.cacheFn.length > 0) {
const fn = _this.cacheFn.shift();
const result = fn(data);
return _this.resultHandler(result);
}
}
function reject(error) {
if (_this.errorObj.fn) {
_this.errorObj.fn(error);
} else {
_this.status = "rejected";
_this.errorObj.error = error;
}
}
try {
fn(resolve, reject);
} catch (error) {
reject(error);
}
}
_Promise.prototype.resultHandler = function (result, isThen) {
if (result instanceof _Promise) {
if (result.errorObj.error !== undefined) {
this.errorObj.error = result.errorObj.error;
}
result.errorObj = this.errorObj;
if (result.status === "resolved") {
const thenFun = this.cacheFn.shift();
result.cacheFn = this.cacheFn;
result.then(thenFun);
} else if (result.status === "pending") {
if (isThen) {
if (this.cacheFn.length > 0) {
result.cacheFn = this.cacheFn;
} else {
return result;
}
} else {
result.cacheFn = this.cacheFn;
}
} else if (result.status === "rejected") {
if (result.errorObj && result.errorObj.fn) {
result.errorObj.fn(result.errorObj.error);
}
}
}
}
_Promise.prototype.catch = function (fn) {
if (typeof fn !== "function") {
return false;
}
if (this.errorObj) {
this.errorObj.fn = fn;
}
}
_Promise.prototype.then = function (fn) {
if (this.status === "resolved") {
const result = fn(this.data);
return this.resultHandler(result, true);
} else if (this.status === "pending") {
this.cacheFn.push(fn);
}
return this;
}
_Promise.prototype.all = function (fn_arr) {
if (!Array.isArray(fn_arr)) {
throw new Error("参数必须要传递一个数组!");
}
let arr = [];
let cache_resolve = null;
let cache_reject = null;
let hasError = false;
fn_arr.forEach((promise, index) => {
const i = index;
promise.then((value) => {
arr[i] = value;
if (arr.length === fn_arr.length) {
cache_resolve(arr);
}
}).catch((e) => {
if (hasError) {
return false;
}
hasError = true;
cache_reject(e);
})
});
return new _Promise((resolve, reject) => {
cache_resolve = resolve;
cache_reject = reject;
});
}

验证代码

        function test(value) {
return new _Promise((resolve, reject) => {
setTimeout(() => {
resolve(value);
}, 2000);
});
}
_Promise.all([test(200), test(400), test(700)])
.then((value) => {
console.log(value);
})
.catch((e) => {
console.log(e);
});
test(1000)
.then((value) => {
console.log(1);
return test(value + 100);
})
.then((value) => {
console.log(2);
return new _Promise((resolve, reject) => {
resolve(value + 500);
});
})
.then((value) => {
console.log(3);
return test(value + 100);
})
.then((value) => {
console.log(value);
})
.catch((e) => {
console.log(e);
});

分析

简单模式

实现一个Promise的延时调用的特性很容易。Promise对象的状态有pending、resolved和rejected三种。创建一个Promise时,状态初始化为pending,并赋予data属性为null。
当用户在Promise函数中执行同步操作时,逻辑比较简单。执行完毕后调用resolve函数,并将返回值赋予this.data。随后,用户调用then函数时,可以直接访问this.data进行操作。
如果是异步操作,则需要在Promise的构造函数中异步调用fn。由于是异步,需要将then函数中的处理逻辑存入缓存(cacheFn),待resolve函数异步完成后,依次执行缓存中的函数,实现链式调用的特性。

链式调用

链式调用是Promise的一大特性。开发者可以写如then(fn1).then(fn2).then(fn3)的代码。实现这一特性,关键在于如何保证多个then函数的正确执行顺序。
我们通过函数柯里化的思想,每个then函数返回一个新的Promise对象,这样下一个then函数可以继续使用then方法。这样实现了链式调用的模式。
在实现过程中,遇到的难点是异步操作的处理。由于resolve函数是异步的,必须在它执行完毕后才能处理then函数中的逻辑。为了保证链式调用顺序,可以将then函数中的函数存入cacheFn数组,待resolve完成后,依序执行cacheFn中的函数。
这种方式确保了每个then函数可以正确继承上一个Promise的上下文以及缓存信息,从而实现了链式调用的效果。

核心实现思路

Promise的实现关键在于如何处理异步操作的执行顺序以及链式调用带来的递归问题。通过缓存函数(cacheFn)和错误对象(errorObj),可以逐步传递执行上下文。
每次then函数被调用时,若是异步操作,立即存入cacheFn;待resolve函数完成后,依次从cacheFn中取出函数执行。这样可以确保函数的执行顺序,准确实现链式调用。
需要注意的是,当返回值是一个Promise对象时,必须处理其状态并继续执行缓存中的下一个函数。只有这样,才能保证链式调用的连贯性和正确性。

总结

《通过对Promise实现的深入分析,可以看出其核心在于通过缓存函数和错误处理,实现异步操作的同步执行顺序和链式调用的特性。虽然逻辑较为复杂,但通过合理的设计,可以实现高效且简洁的异步代码编写。理解这一点对于优化代码结构和提升开发效率至关重要。

转载地址:http://oxxqz.baihongyu.com/

你可能感兴趣的文章
Node-RED中Switch开关和Dropdown选择组件的使用
查看>>
Node-RED中使用html节点爬取HTML网页资料之爬取Node-RED的最新版本
查看>>
Node-RED中使用JSON数据建立web网站
查看>>
Node-RED中使用json节点解析JSON数据
查看>>
Node-RED中使用node-random节点来实现随机数在折线图中显示
查看>>
Node-RED中使用node-red-browser-utils节点实现选择Windows操作系统中的文件并实现图片预览
查看>>
Node-RED中使用node-red-contrib-image-output节点实现图片预览
查看>>
Node-RED中使用node-red-node-ui-iframe节点实现内嵌iframe访问其他网站的效果
查看>>
Node-RED中使用Notification元件显示警告讯息框(温度过高提示)
查看>>
Node-RED中使用range范围节点实现从一个范围对应至另一个范围
查看>>
Node-RED中实现HTML表单提交和获取提交的内容
查看>>
Node-RED中将CSV数据写入txt文件并从文件中读取解析数据
查看>>
Node-RED中建立TCP服务端和客户端
查看>>
Node-RED中建立Websocket客户端连接
查看>>
Node-RED中建立静态网页和动态网页内容
查看>>
Node-RED中解析高德地图天气api的json数据显示天气仪表盘
查看>>
Node-RED中连接Mysql数据库并实现增删改查的操作
查看>>
Node-RED中通过node-red-ui-webcam节点实现访问摄像头并截取照片预览
查看>>
Node-RED中配置周期性执行、指定时间阶段执行、指定时间执行事件
查看>>
Node-RED安装图形化节点dashboard实现订阅mqtt主题并在仪表盘中显示温度
查看>>