来掘金已经一年多了,一直处于潜水的状态,除了看看技术大佬的文章,自己却从来没有写过对技术知识的一些总结和感悟,想来真是惭愧。最近又复习了一下Promise,就把第一篇文章写给Promise吧,嗯,promise的意思是承诺(嗯,我是英语专业的┭┮﹏┭┮)。废话不多说,开始进入正题。
先说说回调地狱
我们在进行js异步编程时,经常会用到回调函数,为了实现某些逻辑时,就会出现回调函数的层层嵌套。直接上代码来看一下:
const sayHello = function (name, callback) {
setTimeout(() => { console.log(name, callback); }, 1000);
}
sayHello('cat', function () {
sayHello('dog', function () {
sayHello('rabbit', function () {
sayHello('sheep', function () {
sayHello('bird', function () {
console.log('套娃结束了')
})
})
})
})
})
有没有感觉这种代码很恐怖啊,就是我们常说的回调地狱,俗称套娃。
回调地狱使得代码的阅读性变得非常差,而且每个回调函数中,我们都需要对错误进行处理,这样也会导致大量的重复代码出现。
Promise的出现
“铛铛铛铛”~~~ Promise要闪现登场了,就是来解决回调地狱的,它是异步编程的一种解决方案,Promise的本质是一个构造函数,所以我们自然就要通过 new 来构造一个Promise对象了哦~
所以改造后的代码是这样的:
const sayHello = function (name) {
return new Promise((resolve, reject) => {
setTimeout(() => { console.log(name); resolve(); }, 1000);
})
}
sayHello('cat').then(function () {
return sayHello('dog');
}).then(function () {
return sayHello('rabbit');
}).then(function () {
return sayHello('sheep');
}).then(function () {
return sayHello('bird');
}).then(function () {
console.log('套娃结束了');
}).catch(function (error) { console.log(error); })
这样写了之后看起比回调地狱那种“套娃”好多了。今天的重点是我要手写一个promise。
先说说几点:
-
promise接收一个执行器函数(暂且叫executor吧),这个执行器函数的代码是同步执行的。
-
执行器函数里面需要定义一个挂起状态:pending。也就是说我们总是要把做的事情从挂起状态推向解决状态,这里的解决状态分为两种,一种是成功状态:resolved,另一种是失败状态:rejected。
-
executor函数接收两个参数:resolve和reject,这两个参数也是函数,可以理解为回调函数吧,将事情的结果放入resolve函数或reject函数的参数中。
废话不多说,手写一个promise来看下:
(function (window) {
function Promise(executor) { //接收executor执行器函数作为参数,executor是同步执行
const self = this;
self.status = 'pending';
self.data = undefined; //假设数据一开始是undefined的
self.callbacks = []; //将回调函数放进数组中,回调函数的数据结构:{ onResolved() {}, onRejected() {} }
function resolve(value) { //异步操作成功的函数,参数value就是data数据最终变成的结果数据
if (self.status !== 'pending') return;
self.status = 'resolved';
self.data = value;
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbackObj => {
callbackObj.onResolved(value);
})
});
}
}
function reject(value) { //异步操作失败的函数
if (self.status !== 'pending') return;
self.status = 'rejected';
self.data = value;
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbackObj => {
callbackObj.onRejected(value);
})
});
}
}
//立即同步执行executor函数
try {
executor(resolve, reject)
} catch (error) {
reject(error); //如果执行器抛出异常,promise对象变为rejected状态
}
}
Promise.prototype.then = function(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : value => value; //向后成功传递的value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw error}; //向后传递失败的error
const self = this;
return new Promise((resolve, reject) => {
//调用指定回调函数处理
function handle(callback) {
/**
* 根据回调函数的返回值处理:
* 1. 返回值是promise,return的promise结果就是这个promise的结果
* 2. 返回值不是promise,返回的promise会成功,返回值是value
* 3. 直接抛出错误,return的promise会失败,reason就是error
*/
try {
const result = callback(self.data);
if(result instanceof Promise) { //回调函数返回值是promise
result.then(resolve, reject);
} else { //回调函数返回值不是promise
resolve(result);
}
} catch (error) {
reject(error);
}
}
if(self.status === 'pending') { //如果当前状态是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(value) {
handle(onResolved);
},
onRejected(reason) {
handle(onRejected);
}
})
} else if(self.status === 'resolved') { //假设当前状态是resolved状态
setTimeout(() => {
handle(onResolved);
});
} else { //假设当前是rejected状态
setTimeout(() => {
handle(onRejected);
});
}
})
}
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected);
}
Promise.resolve = function(value) {
//返回一个成功/失败的promise
return new Promise((resolve, reject) => {
/**
* 1. 参数是一个promise
* 2. 参数直接是一个值
*/
if(value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value);
}
})
}
Promise.reject = function(reason) {
//返回一个失败的promise
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all = function(promises) {
let resolvedCount = 0;
const values = new Array(promises.length);
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
Promise.resolve(p).then(
value => {
resolvedCount ++;
values[index] = value;
if(resolvedCount === promises.length) {
resolve(values);
}
}
),
reason => {
reject(reason);
}
})
})
}
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
Promise.resolve(p).then(
value => {
resolve(value);
}
)
}),
reason => {
reject(reason);
}
})
}
window.Promise = Promise;
})(window)