精选之Promise
我们从promise是什么怎么会出现来一步步解决问题来深入了解
1、了解 Promise 吗?
2、Promise 解决的痛点是什么?
3、Promise 解决的痛点还有其他方法可以解决吗?如果有,请列举。
4、Promise 如何使用?
5、Promise 常用的方法有哪些?它们的作用是什么?
6、Promise 在事件循环中的执行过程是怎样的?
7、Promise 的业界实现都有哪些?
8、能不能手写一个 Promise 的 polyfill。
Promise 出现的原因
在 Promise 出现以前,我们处理一个异步网络请求,大概是这样:
// 请求 代表 一个异步网络调用。
// 请求结果 代表网络请求的响应。
请求1(function(请求结果1){
处理请求结果1
})
需求变化了,我们需要根据第一个网络请求的结果,再去执行第二个网络请求,代码大概如下:
请求1(function(请求结果1){
请求2(function(请求结果2){
处理请求结果2
})
})
请求1(function(请求结果1){
请求2(function(请求结果2){
请求3(function(请求结果3){
请求4(function(请求结果4){
请求5(function(请求结果5){
请求6(function(请求结果3){
...
})
})
})
})
})
})
看到以上这种代码写法,应该明白这个写法的不好处吧,可维护性太差了,这个就是所谓的 回调地狱
更糟糕的是,我们基本上还要对每次请求的结果进行一些处理,代码会更加臃肿,在一个团队中,代码 review 以及后续的维护将会是一个很痛苦的过程
回调地狱带来的负面作用有以下几点:
代码臃肿。
可读性差。
耦合度过高,可维护性差。
代码复用性差。
容易滋生 bug。
只能在回调里处理异常。
那出现问题总会有牛人来想出更好的办法和方案给我们后来的菜鸟使用,会有人思考可不可以有这种简洁的写法来实现
let 请求结果1 = 请求1();
let 请求结果2 = 请求2(请求结果1);
let 请求结果3 = 请求3(请求结果2);
let 请求结果4 = 请求2(请求结果3);
let 请求结果5 = 请求3(请求结果4);
接下来promise的出现
什么是 Promise?
new Promise(请求1)
.then(请求2(请求结果1))
.then(请求3(请求结果2))
.then(请求4(请求结果3))
.then(请求5(请求结果4))
.catch(处理异常(异常信息))
Promise 的常用 API 如下:
Promise.resolve(value)
返回一个状态已变成 resolved 的 Promise 对象。
Promise.reject
类方法,且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected。
Promise.prototype.then
实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收
Promise.prototype.catch
实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息
Promise.race
类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败。 。
Promise.all
类方法,多个 Promise 任务同时执行。
如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。
如何更好的理解promise,看到过一个比较有趣更能说明promise的例子引用来
我们可以把 Promise 比作一个保姆,家里的一连串的事情,你只需要吩咐给他,他就能帮你做,你就可以去做其他事情了。
比如,作为一家之主的我,某一天要出门办事,但是我还要买菜做饭送到老婆单位(请理解我在家里的地位。。)
出门办的事情很重要,买菜做饭也重要。。但我自己只能做一件事。
这时我就可以把买菜做饭的事情交给保姆,我会告诉她:
你先去超市买菜。
用超市买回来的菜做饭。
将做好的饭菜送到老婆单位。
送到单位后打电话告诉我。
我们知道,上面三步都是需要消耗时间的,我们可以理解为三个异步任务。利用 Promise 的写法来书写这个操作:
// 告诉保姆帮我做几件连贯的事情,先去超市买菜
new Promise(买菜)
//用买好的菜做饭
.then((买好的菜)=>{
return new Promise(做饭);
})
//把做好的饭送到老婆公司
.then((做好的饭)=>{
return new Promise(送饭);
})
//送完饭后打电话通知我
.then((送饭结果)=>{
电话通知我();
})
请一定要谨记:如果我们的后续任务是异步任务的话,必须return 一个 新的 promise 对象。
如果后续任务是同步任务,只需 return 一个结果即可。
我们上面举的例子,除了电话通知我是一个同步任务,其余的都是异步任务,异步任务 return 的是 promise对象。
一个 Promise 对象有三个状态,并且状态一旦改变,便不能再被更改为其他状态。
pending,异步任务正在进行。
resolved (也可以叫fulfilled),异步任务执行成功。
rejected,异步任务执行失败。
Promsie 与事件循环
Promise在初始化时,传入的函数是同步执行的,然后注册 then 回调。注册完之后,继续往下执行同步代码,在这之前,then 中回调不会执行。同步代码块执行完毕后,才会在事件循环中检测是否有可用的 promise 回调,如果有,那么执行,如果没有,继续下一个事件循环。
async await是语法糖,async/await也是基于 Promise 和Generator生成器函数
(async ()=>{
let 蔬菜 = await 买菜();
let 饭菜 = await 做饭(蔬菜);
let 送饭结果 = await 送饭(饭菜);
let 通知结果 = await 通知我(送饭结果);
})();
promise 术语
1,解决 (fulfill) : 指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
2,拒绝(reject) : 指一个 promise 失败时进行的一系列操作。
拒因 (reason) : 也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
3,终值(eventual value) : 所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
4,Promise : promise 是一个拥有 then 方法的对象或函数,其行为符合本规范。
5,thenable : 是一个定义了 then 方法的对象或函数,文中译作“拥有 then 方法”。
6,异常(exception) : 是使用 throw 语句抛出的一个值。
下面我们先来讲述Promise/A+ 规范的几个基本要求
一个Promise的当前状态必须是以下三种状态中的一种: 等待状态(Pending) 执行状态(Fulfilled) 和 拒绝状态(Rejected)。
const PENDING = ‘pending’;
const FULFILLED = ‘fulfilled’;
const REJECTED = ‘rejected’;
等待状态 (Pending)
处于等待态时,promise 需满足以下条件:
可以迁移至执行态或拒绝态
if (this.state === PENDING) {
this.state = FULFILLED || REJECTED ;
}
执行状态 (Fulfilled)
处于执行态时,promise 需满足以下条件:
不能迁移至其他任何状态
必须拥有一个不可变的终值
this.value = value;
拒绝状态 (Rejected)
处于拒绝态时,promise 需满足以下条件:
不能迁移至其他任何状态
必须拥有一个不可变的据因
this.reason = reason;
- Then 方法
一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
参数可选
onFulfilled 和 onRejected 都是可选参数。
如果 onFulfilled 不是函数,其必须被忽略
如果 onRejected 不是函数,其必须被忽略
onFulfilled 特性
如果 onFulfilled 是函数:
当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值
在 promise 执行结束前其不可被调用
其调用次数不可超过一次
onRejected 特性
如果 onRejected 是函数:
当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因
在 promise 被拒绝执行前其不可被调用
其调用次数不可超过一次
调用时机
onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
这个事件队列可以采用“宏任务(macro - task)”机制或者“微任务(micro - task)”机制来实现。
由于 promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。
调用要求
onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)
多次调用
then 方法可以被同一个 promise 调用多次
当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调
简单的promise实现
//Promise 的三种状态 (满足要求 -> Promise的状态)
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class AjPromise {
constructor(fn) {
//当前状态
this.state = PENDING;
//终值
this.value = null;
//拒因
this.reason = null;
//成功态回调队列
this.onFulfilledCallbacks = [];
//拒绝态回调队列
this.onRejectedCallbacks = [];
//成功态回调
const resolve = value => {
// 使用macro-task机制(setTimeout),确保onFulfilled异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
setTimeout(() => {
if (this.state === PENDING) {
// pending(等待态)迁移至 fulfilled(执行态),保证调用次数不超过一次。
this.state = FULFILLED;
// 终值
this.value = value;
this.onFulfilledCallbacks.map(cb => {
this.value = cb(this.value);
});
}
});
};
//拒绝态回调
const reject = reason => {
// 使用macro-task机制(setTimeout),确保onRejected异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 (满足要求 -> 调用时机)
setTimeout(() => {
if (this.state === PENDING) {
// pending(等待态)迁移至 fulfilled(拒绝态),保证调用次数不超过一次。
this.state = REJECTED;
//拒因
this.reason = reason;
this.onRejectedCallbacks.map(cb => {
this.reason = cb(this.reason);
});
}
});
};
try {
//执行promise
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
typeof onFulfilled === 'function' && this.onFulfilledCallbacks.push(onFulfilled);
typeof onRejected === 'function' && this.onRejectedCallbacks.push(onRejected);
// 返回this支持then 方法可以被同一个 promise 调用多次
return this;
}
}
Promises/A+ 规范 完整代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class AjPromise {
constructor(fn) {
this.state = PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(() => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.map(cb => {
cb = cb(this.value);
});
}
});
};
const reject = reason => {
setTimeout(() => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.map(cb => {
cb = cb(this.reason);
});
}
});
};
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
let newPromise;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: reason => {
throw reason;
};
if (this.state === FULFILLED) {
return (newPromise = new AjPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
if (this.state === REJECTED) {
return (newPromise = new AjPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
if (this.state === PENDING) {
return (newPromise = new AjPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
reject(new TypeError('循环引用'));
}
if (x instanceof AjPromise) {
if (x.state === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject);
},
reason => {
reject(reason);
}
);
} else {
x.then(resolve, reject);
}
} else if (x && (typeof x === 'function' || typeof x === 'object')) {
let called = false;
try {
let then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
AjPromise.deferred = function() {
let defer = {};
defer.promise = new AjPromise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
};
module.exports = AjPromise;
版权声明:本文由Web学习之路发布,如需转载请注明出处。