从0到1搞一哈promise
1、发布订阅 on-emit
发布订阅模式需要有一个消息管理器
,发布者并不会直接去联系订阅者,他们之间有一个中间人
来处理消息队列(函数)。 发布者和订阅者是解耦的。
// 发布订阅
let e = {
arr: [],
on(fn){
this.arr.push(fn);
},
emit(){
this.arr.forEach((fn) => fn());
}
}
// 订阅
e.on( ()=> {
console.log('1');
})
// 发布
function something (){
e.emit();
}
something();
2、观察者模式 observer
发布订阅是观察者模式的一部分,观察者模式分两个角色,观察者和被观察者,被观察者状态发生改变之后通知自己身上的所有观察者更新状态。观察者和被观察者是有联系的。
class Student { // 被观察者
constructor(){
this.teachers = []; // 存放订阅/观察者
this.state = '认真学习'; // 状态
}
attach(t){
this.teachers.push(t); // 订阅
}
setState(newState){
this.state = newState; // 修改被观察者的状态
this.teachers.forEach((t) => t.update(newState)) // 被观察者的状态改变后通知观察者
}
}
class Teacher {
constructor(name){
this.name = name
}
update(newState){
console.log(this.name + '的学生' + newState + '了,要做某事了');
}
}
let s = new Student();
let t1 = new Teacher('张老师');
let t2 = new Teacher('李老师');
// 订阅, 把观察者放到被观察者身上去
s.attach(t1);
s.attach(t2);
s.setState('谈恋爱');
// 张老师的学生谈恋爱了,要做某事了
// 李老师的学生谈恋爱了,要做某事了
3、promise
3.1 promise的作用
解决并发问题:同步获取多个异步的执行结果。
链式调用问题(多个回调嵌套):下一个接口的参数依赖于上一个接口的返回值
3.2 promise的使用
promise是一个类
每次new promise都需要传一个执行器函数,这个函数是立即实行的
执行器函数有两个参数 resolve(成功) reject(失败)
promise默认有三个状态, pending(等待) resolve(成功) reject(失败)
状态一旦改变,不能再次改变,改变会被忽略。
每个promise都有
then
方法,还可以链式调用.then().then()
多次。promise实例可以调用
then
方法去监控成功或者失败的回调(resolve or reject), 第一个参数是成功的回调,第二个参数是失败的回调。
let p = new Promise((resolve, reject) => {
// 只能是成功或者失败之一,因为状态一旦改变,不能再次改变。
resolve('成功了');
// reject('失败了');
})
p.then((data) => {
console.log(data);
}, (err) => {
console.log(err);
})
3.3 简易版promise实现
根据上面的规则我们可以写出来一个简易版的promise,不处理异步情况,不处理链式调用。
// promise 的三个状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 定义成功或者失败的值
this.value = undefined;
this.reason = undefined;
// 定义状态
this.status = PENDING;
let resolve = (value)=>{
// 只有当状态为PENDING的时候才可以修改状态
if(this.status === PENDING){
this.value = value;
this.status = FULFILLED;
}
}
let reject = (reason)=>{
// 只有当状态为PENDING的时候才可以修改状态
if(this.status === PENDING){
this.reason = reason;
this.status = REJECTED;
}
}
// 每个promise有两个参数,成功or失败,都为函数
// 这里可能会发生异常
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// .then 接受两个参数,一个成功的回调,一个失败的回调。判断当前的状态 如果状态为成功则调用成功的回调参数,否则调失败的
then(onFulfilled, onRejected){
if(this.status === FULFILLED){
onFulfilled(this.value);
}
if(this.status === REJECTED){
onRejected(this.reason);
}
}
}
// 调用
let p = new Promise((res, rej)=>{
res('hi')
})
p.then(data => {
console.log(data)
}, err=>{
console.log(err)
})
3.4 处理异步的promise
使用发布订阅模式处理异步请求,不处理链式调用
// promise 的三个状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 定义成功或者失败的值
this.value = undefined;
this.reason = undefined;
// 定义状态
this.status = PENDING;
// 使用发布订阅模式来做异步处理,先定义成功和失败的序列
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value)=>{
// 只有当状态为PENDING的时候才可以修改状态
if(this.status === PENDING){
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason)=>{
// 只有当状态为PENDING的时候才可以修改状态
if(this.status === PENDING){
this.reason = reason;
this.status = REJECTED;
// 可能会有之前存起来的订阅(回调),把他发布了(执行)
this.onRejectedCallbacks.forEach(fn => fn());
}
}
// 每个promise有两个参数,成功or失败,都为函数
// 这里可能会发生异常
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// .then 接受两个参数,一个成功的回调,一个失败的回调。判断当前的状态 如果状态为成功则调用成功的回调参数,否则调失败的
then(onFulfilled, onRejected){
if(this.status === FULFILLED){
onFulfilled(this.value);
}
if(this.status === REJECTED){
onRejected(this.reason);
}
// 如果异步调用了,这个时候promise的状态是pending
if(this.status === PENDING){
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason);
})
}
}
}
3.5 完整的promise
promise链式调用:p().then().then()....
;
每一次.then
如果返回的是普通值(普通值:不是promise也不是错误)就会继续走下一个.then()
的成功,如果有返回值,则当前的返回值是下一个then
的参数,没有的话为undefind
。
如果返回的是一个promise
,那么这个promise
会立即执行并且采用该promise的状态
,把结果传给外层的.then()
里面,如果是成功就走成功,失败就走失败。
如果抛出了错误,自己有错误的回调监听走自己的,没有则走下一个.then
的失败。
每个then
返回一个新的promise
。
所以如果想让下一个then
为error
的话,需要返回一个失败的promise 或 抛出异常
。
// promise 的三个状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
// promise处理函数
const resolvePromise = (promise2, x, resolve, reject) => {
//
if(promise2 === x){ // 如果引用相等,则说明是同一个对象,抛出错误
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// 如果当前x是对象,但是不为null 或 x 是function的话
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
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);
}
}
class Promise {
constructor(executor) {
// 定义成功或者失败的值
this.value = undefined;
this.reason = undefined;
// 定义状态
this.status = PENDING;
// 使用发布订阅模式来做异步处理,先定义成功和失败的序列
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
// 如果一个promise resolve一个新的promise,会等到这个内部的promise执行完成,reject会直接失败
if(value instanceof Promise){
return value.then(resolve, reject);
}
let resolve = (value) => {
// 只有当状态为PENDING的时候才可以修改状态
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
let reject = (reason) => {
// 只有当状态为PENDING的时候才可以修改状态
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
// 可能会有之前存起来的订阅(回调),把他发布了(执行)
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 每个promise有两个参数,成功or失败,都为函数
// 这里可能会发生异常
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// .then 接受两个参数,一个成功的回调,一个失败的回调。判断当前的状态 如果状态为成功则调用成功的回调参数,否则调失败的
then(onFulfilled, onRejected) {
// 选参
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : error => {throw error};
// then方法调用后应该返回一个新的promise
// 每次new promise的时候,promise的控制器立即执行
let promise2 = new Promise((resolve, reject) => {
// 把当前的值传递给下一个promise里面
if (this.status === FULFILLED) {
// 当前promise2还没有值,所以需要异步一下获取
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
// 如果异步调用了,这个时候promise的状态是pending
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
})
}
})
return promise2;
}
catch(errCallback){ // 没有成功的then
return this.then(null,errCallback)
}
static reject(reason){ // 创建了一个失败的promise
return new Promise((resolve,reject)=>{
reject(reason);
})
}
static resolve(value){ // 创建了一个成功的promise
return new Promise((resolve,reject)=>{
resolve(value);
})
}
}
4、promise.all()
全部完成才算完成,如果有一个失败就失败,用来处理多个异步并发问题。
promise.all是按照顺序执行的。
Promise.all = function(promises) {
return new Promise((resolve, reject)=>{
let arr = []; //存放最后的结果
let index = 0;
let processData = (i, data)=>{
arr[i] = data;
if(++index === promises.length){
resolve(arr);
}
}
for(let i = 0; i<promises.length; i++){
let current = promises[i];
if((current.then && typeof current.then === 'object') || typeof current.then === 'function'){
current.then((data)=>{
processData(i, data);
}, reject)
} else {
processData(i, current);
}
}
})
}
let p1 = new Promise(function (resolve, reject) {
resolve(1);
})
let p2 = new Promise(function (resolve, reject) {
resolve(2);
})
let p3 = new Promise(function (resolve, reject) {
resolve(3);
})
let p4 = Promise.all([p1, p2, p3]);
p4.then(function (value) {
console.log(value); // [1,2,3];
})
原理:
- promise.all()返回一个新的promise
- promise.all()等待所有异步方法都完成才返回,如果有一个失败就失败
- 根据上面的情况,我们需要一个计数器
index
,一个数据的收集数组arr
- 如果计数器的数量等于参数的数量则返回
arr
- 如果某一项为普通值的话根据promise A+走
resolve
; - 如果某一项执行失败,则直接返回
reject
5、promise.race();
要求传入的值为一个可序列化的值,我这里就简单要求就是一个数组。
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。 – MDN
Promise.race = function(promises) {
return new Promise((resolve, reject)=>{
if(!Array.isArray(promises)){
throw new TypeError('argument should be an array')
}
for(let i = 0; i<promises.length; i++){
let current = promises[i];
if((current.then && typeof current.then === 'object') || typeof current.then === 'function'){
current.then(resolve, reject)
} else {
resolve(current)
}
}
})
}
let p1 = new Promise(function (resolve, reject) {
setTimeout(()=>{
resolve(1);
},200)
})
let p2 = new Promise(function (resolve, reject) {
resolve(2);
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(()=>{
resolve(3);
},500)
})
let p4 = Promise.race([p1, p2, p3]);
p4.then(function (value) {
console.log(value); // 2
})
原理:
- 最先执行完毕的那个是成功就返回成功,失败就返回失败。
- 先判断是不是普通值,如果是直接成功。
- 如果不是普通值,去执行,哪个先执行完就返回哪个。
6、promise.finally()
该方法不管promise成功还是失败都会执行。
Promise.resolve()会等待里面的promise完成后执行完成。
Promise.prototype.finally= callback => {
return this.then((val)=>{
// 等待finally里面的函数执行完毕之后执行, finally可能返回一个promise
return Promise.resolve(callback()).then(() => val);
}, (err)=>{
return Promise.resolve(callback()).then(() => {throw err});
})
}
7、promise.try()
捕获错误,不论是同步还是异步。
Promise.try= callback => {
return new Promise((resolve, reject)=>{
return promise.resolve(callback()).then(resolve, reject);
})
}