热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Promise:流程与源码实现

本文探讨了异步编程的发展历程,从最初的AJAX异步回调到现代的Promise、Generator+Co以及Async/Await等技术。文章详细分析了Promise的工作原理及其源码实现,帮助开发者更好地理解和使用这一重要工具。

异步编程的发展历程

早期的异步编程主要依赖于AJAX异步回调,即在异步请求中嵌入一个回调函数来处理响应。这种方法虽然实现了基本的异步功能,但也带来了诸如“回调地狱”等问题。随着技术的发展,Promise、Generator+Co以及Async/Await等更先进的解决方案逐渐出现,极大地简化了异步编程的复杂性。

源码解析

// 基本Promise用法
let promise = new Promise((resolve, reject) => {
// 执行函数,包含成功和失败两种情况
});
promise.then(
(res) => { /* 处理成功 */ },
(err) => { /* 处理失败 */ }
);
// 以下是Promise的核心实现
function Promise(executor) {
let self = this;
self.status = 'pending'; // 初始状态
self.value = undefined; // 成功时的值
self.reason = undefined; // 失败时的原因
function resolve(value) {
if (self.status === 'pending') {
self.value = value;
self.status = 'resolved';
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected';
}
}
executor(resolve, reject);
}
// 实现then方法
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
if (self.status === 'resolved') {
onFulfilled(self.value);
}
if (self.status === 'rejected') {
onRejected(self.reason);
}
};
// 导出模块
module.exports = Promise;

异步处理的改进

实际应用中,异步操作通常不会立即完成,因此需要对上述代码进行改进,以支持异步操作。

// 引入回调队列
function Promise(executor) {
let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
self.OnResolvedCallbacks= [];
self.OnRejectedCallbacks= [];
function resolve(value) {
if (self.status === 'pending') {
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(fn => fn());
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(fn => fn());
}
}
executor(resolve, reject);
}
// 改进then方法
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
if (self.status === 'pending') {
self.onResolvedCallbacks.push(() => onFulfilled(self.value));
self.onRejectedCallbacks.push(() => onRejected(self.reason));
}
if (self.status === 'resolved') {
onFulfilled(self.value);
}
if (self.status === 'rejected') {
onRejected(self.reason);
}
};

小结:为了确保异步操作的正确执行,每个Promise的then方法都需要将回调函数放入队列中,待状态改变时依次执行。此外,为了符合Promises/A+规范,所有操作应包裹在setTimeout中,以确保异步执行。

进一步完善Promise实现

除了基本的异步处理外,还需要考虑一些特殊情况,如异常处理、链式调用等。

// 异常处理
function Promise(executor) {
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 链式调用
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
let promise2;
if (self.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
onFulfilled(self.value);
});
});
}
if (self.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
onRejected(self.reason);
});
});
}
if (self.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
self.onResolvedCallbacks.push(() => {
onFulfilled(self.value);
});
self.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
});
}
return promise2;
};
// 处理返回自身和其他特殊情况
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
let called;
if ((x !== null) && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if (called) return;
called = true;
reject(err);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
// 测试Promise是否符合规范
npm install -g promises-aplus-tests
promises-aplus-tests ./Promise.js

扩展内容

除了基本的Promise实现,还有一些高级技巧可以帮助开发者更好地利用这一工具。

高阶函数

// 判断数据类型
function isType(type) {
return function(content) {
return Object.prototype.toString.call(content) === `[object ${type}]`;
};
}
let isString = isType('String');
let isArray = isType('Array');
console.log(isArray('hello')); // false
// 预置函数
function after(times, callback) {
return function() {
if (--times === 0) {
callback();
}
};
}
let eat = after(3, () => {
console.log('不吃了');
});
eat(); // 没打印
eat(); // 没打印
eat(); // 打印 '不吃了'

通过这些高阶函数,可以更灵活地处理各种编程任务,提高代码的复用性和可维护性。


推荐阅读
  • 本文介绍了如何使用Node.js通过两种不同的方法连接MongoDB数据库,包括使用MongoClient对象和连接字符串的方法。每种方法都有其特点和适用场景,适合不同需求的开发者。 ... [详细]
  • 本文详细介绍了如何在 Ubuntu 14.04 系统上搭建仅使用 CPU 的 Caffe 深度学习框架,包括环境准备、依赖安装及编译过程。 ... [详细]
  • 利用Node.js实现PSD文件的高效切图
    本文介绍了如何通过Node.js及其psd2json模块,快速实现PSD文件的自动化切图过程,以适应项目中频繁的界面更新需求。此方法不仅提高了工作效率,还简化了从设计稿到实际应用的转换流程。 ... [详细]
  • 本文介绍了.hbs文件作为Ember.js项目中的视图层,类似于HTML文件的功能,并详细讲解了如何在Ember.js应用中集成Bootstrap框架及其相关组件的方法。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 在项目需要国际化处理时,即支持多种语言切换的功能,通常有两种方案:单个包和多个包。本文将重点讨论单个包的实现方法。 ... [详细]
  • 本文将详细介绍如何在 Vue 项目中使用 Handsontable 插件,包括 npm 安装、基本配置和常用功能的实现。 ... [详细]
  • ArcBlock 发布 ABT 节点 1.0.31 版本更新
    2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
  • 基于SSM框架的在线考试系统:随机组卷功能详解
    本文深入探讨了基于SSM(Spring, Spring MVC, MyBatis)框架构建的在线考试系统中,随机组卷功能的设计与实现方法。 ... [详细]
  • 本文深入探讨了Go语言中的接口型函数,通过实例分析其灵活性和强大功能,帮助开发者更好地理解和运用这一特性。 ... [详细]
  • 1.绑定htmlcss1.1对象语法:  传给v-bind:class一个对象,以动态地切换class   ... [详细]
  • 理解浏览器历史记录(2)hashchange、pushState
    阅读目录1.hashchange2.pushState本文也是一篇基础文章。继上文之后,本打算去研究pushState,偶然在一些信息中发现了锚点变 ... [详细]
  • Spring Data JdbcTemplate 入门指南
    本文将介绍如何使用 Spring JdbcTemplate 进行数据库操作,包括查询和插入数据。我们将通过一个学生表的示例来演示具体步骤。 ... [详细]
  • 本文详细解析了ASP.NET 2.0中的Callback机制,不仅介绍了基本的使用方法,还深入探讨了其背后的实现原理。通过对比Atlas框架,帮助读者更好地理解和应用这一机制。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
author-avatar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有