前言
Promise
是一种异步编程的解决方案,可以认为它是一个容器,里面保存着未来发生的事件结果。 它有三种状态:pending(进行中)
、fulfilled(已成功)
和rejected(已失败)
,状态一旦发生改变就不能再次改变。
什么是回调地狱?
在处理异步请求时,我们一般都会使用回调函数这么处理,这么看完全没毛病,逻辑清晰。
http.post(data,function(res) {
// do something here
})
但是如果我们需要根据前一个请求返回的结果来发起下一个请求的话,代码则变成了这样:
http.post(data,function(res1) {
http.post(res1,function(res2) {
// do something here
})
})
随着产品和业务逻辑逐渐复杂,可能就会滋生出这种代码 ( 手动狗头 ) :
http.post(data,function(res1){
http.post(res1,function(res2){
http.post(res2,function(res3){
http.post(res3,function(res4){
http.post(res4,function(res5){
http.post(res5,function(res6){
// do something here
})
})
})
})
})
})
这便是臭名昭著的回调地狱了,带来的负面影响也是不言而喻的:
影响
1,代码臃肿,可读性差
2,耦合程度高,可维护性差
3,只能在回调函数内部处理异常
but… 如果使用 Promise 我们可以写成这样:
fetch(data).then(res1 => {
return fetch(res1);
}).then(res2 => {
return fetch(res2);
}).then(res3 => {
return fetch(res3);
}).catch(err => {
console.error('err: ', err);
})
Promise 有什么不足吗?
Promise 的链式调用可以让代码变得更加直观,虽然相对看起来逻辑清晰点,但依然还是存在then调用链,有代码冗余的问题,还存在以下的不足:
1,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2,如果不设置回调函数,promise内部抛出的错误,不会反应到外部。
3,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成).
这段代码的输出是?为什么?
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
setTimeout(() => {
console.log(2);
})
reject('error');
})
promise.then(() => {
console.log(3);
}).then(() => {
console.log(5)
}).catch(e => console.log(e))
console.log(4);
老生常谈的话题,也就是考察宏任务和微任务。重点主要是:
Promise
函数体内的代码同步执行
先执行宏任务,再执行微任务,执行完微任务后,就再次查找是否有需要执行的宏任务,如此循环往复
Promise
的状态一旦发生变化,就不可以再次改变
正确的输出顺序是1、4、3、5、2
可以手写 Promise 吗?
这里简单的实现一个能够满足then方法链式调用的Promise:
class Promise {
constructor(params) {
//初始化state为pending
this.state = 'pending';
//成功的值,返回一般都是undefined
this.value = undefined;
//失败的原因,返回一般都是undefined
this.reason = undefined;
//成功执行函数队列
this.onResolvedCallbacks = [];
//失败执行函数队列
this.onRejectedCallbacks = [];
//success
let resolve = value => {
if (this.state === 'pending') {
//state change
this.state = 'fulfilled';
//储存成功的值
this.value = value;
//一旦成功,调用函数队列
this.onResolvedCallbacks.forEach(fn => fn());
}
};
//error
let reject = reason => {
if (this.state === 'pending') {
//state change
this.state = 'rejected';
//储存成功的原因
this.reason = reason;
//一旦失败,调用函数队列
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
params(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
let promise2 = new Promise((resolve, reject) => {
//当状态是fulfilled时执行onFulfilled函数
if (this.state === 'fulfilled') {
//异步实现
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
//当状态是rejected时执行onRejected函数
if (this.state === 'rejected') {
//异步实现
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
//当状态是pending时,往onFulfilledCacks、onRejectedCacks里加入函数
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
//异步实现
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
//异步实现
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn) {
return this.then(null, fn);
}
}
function resolvePromise(promise2, x, resolve, reject) {
//循环引用报错
if (x === promise2) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
//防止多次调用
let called;
//判断x
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);
}
}
//resolve方法
Promise.resolve = function (val) {
return new Promise((resolve, reject) => {
resolve(val)
});
}
//reject方法
Promise.reject = function (val) {
return new Promise((resolve, reject) => {
reject(val);
});
}
const test = new Promise((res, rej) => {
setTimeout(() => {
res('resolve after 2000ms');
}, 2000)
})
test.then(res => {
console.error('res: ', res); // res: resolve after 2000ms
})
在这里我们的回调函数用setTimeout实现,把它们放到了宏任务队列里