热门标签 | 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(); // 打印 '不吃了'

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


推荐阅读
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • golang常用库:配置文件解析库/管理工具viper使用
    golang常用库:配置文件解析库管理工具-viper使用-一、viper简介viper配置管理解析库,是由大神SteveFrancia开发,他在google领导着golang的 ... [详细]
  • PyCharm下载与安装指南
    本文详细介绍如何从官方渠道下载并安装PyCharm集成开发环境(IDE),涵盖Windows、macOS和Linux系统,同时提供详细的安装步骤及配置建议。 ... [详细]
  • Java 中的 BigDecimal pow()方法,示例 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 离线环境下的Python及其第三方库安装指南
    在项目开发中,有时会遇到电脑只能连接内网或完全无法联网的情况。本文将详细介绍如何在这种环境下安装Python及其所需的第三方库,确保开发工作的顺利进行。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • 本文探讨了如何优化和正确配置Kafka Streams应用程序以确保准确的状态存储查询。通过调整配置参数和代码逻辑,可以有效解决数据不一致的问题。 ... [详细]
  • 全面解析运维监控:白盒与黑盒监控及四大黄金指标
    本文深入探讨了白盒和黑盒监控的概念,以及它们在系统监控中的应用。通过详细分析基础监控和业务监控的不同采集方法,结合四个黄金指标的解读,帮助读者更好地理解和实施有效的监控策略。 ... [详细]
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社区 版权所有