异步编程的发展历程
早期的异步编程主要依赖于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(); // 打印 '不吃了'
通过这些高阶函数,可以更灵活地处理各种编程任务,提高代码的复用性和可维护性。