Promise 简朴完成
媒介
你能够晓得,javascript 的使命实行的形式有两种:同步和异步。
异步形式非常重要,在浏览器端,耗时很长的操纵(比方 ajax 要求)都应该异步实行,防止浏览器落空相应。
在异步形式编程中,我们常常运用回调函数。一不小心就能够写出以下如许的代码:
//事宜1
doSomeThing1(function() {
//事宜2
doSomeThing2(function() {
//事宜3
doSomeThing3();
});
});
当你的须要异步实行的函数越来越多,你的层级也会越来越深。
如许的写法存在的瑕玷是:
- 不利于浏览
- 各个使命之间的高度耦合,难以保护
- 对非常的处置惩罚比较难
用 Promise 能够防止这类回调地狱,能够写成如许
//事宜1
doSomeThing1()
.then(function() {
//事宜2
return doSomeThing2();
})
.then(function() {
//事宜3
return doSomeThing3();
})
.catch(function() {
//这里能够很轻易的做非常处置惩罚
});
在市情上有很多库都完成了 Promise,比方:Q.js 、when.js ,es6 也将 Promise 纳入了规范中
es6 的 Promise 运用要领能够参考阮一峰的
http://es6.ruanyifeng.com/#do… ,我就不在做详细引见
接下来,我们模仿 ES6 的 promise,一步一步来完成一个简朴版的 Promise。
组织函数
我们运用 Promise 的时刻,
const promise = new Promise((resolve, reject)=>{
// ... some code
if (/* 异步操纵胜利 */){
resolve(value);
} else {
reject(error);
}
});
Promise 是一个组织函数,吸收一个函数,函数里有两个参数,resolve、reject。
我们能够如许子完成:
class PromiseA {
constructor(executor) {
const resolve = value => {
this.resolve(value);
};
const reject = err => {
this.reject(err);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
resolve(value) {
this.resultValue = value;
}
reject(error) {
this.resultValue = error;
}
}
then 要领
promise 中,用的最频仍的就是 then 要领,then 要领有两个参数,一个是 promise 胜利时刻的回调,一个是失利的回调。
完成体式格局为:
class PromiseA {
constructor(executor) {
const resolve = value => {
this.resolve(value);
};
const reject = err => {
this.reject(err);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
resolve(value) {
this.resultValue = value;
if (this.fullfillCallback) {
//++++++++
this.fullfillCallback(value);
}
}
reject(error) {
this.resultValue = error;
if (this.rejectCallback) {
//++++++++
this.rejectCallback(value);
}
}
then(resolve, reject) {
this.fullfillCallback = resolve;
this.rejectCallback = resolve;
}
}
then 要领有以下几个特征:
- 支撑链式操纵
- 每次 then 要领都是返回新的 Promise
- 当前 promise 的状况经由过程返回值传递给下一个 promise
- 毛病冒泡,即假如当前 promise 没有供应 onReject 要领,会把毛病冒泡到下一个 promise,轻易处置惩罚
then(onResolve,onReject){
//返回新的Promise并支撑链式操纵
return new PromiseA((resolve,reject)=>{
this.fullfillCallback = (value)=>{
try {
if (onResolve) {
let newValue = onResolve(value);
resolve(newValue); //将当前promise实行效果,传递给下一个promise
} else {
resolve(value);
}
} catch (err) {
reject(err);
}
}
//相似fullfillCallback
this.rejectCallback = (value)=>{
try {
if (onReject) {
let newValue = onReject(value);
resolve(newValue);
} else {
//毛病冒泡
reject(value);
}
} catch (err) {
reject(err);
}
}
});
}
如许我们就完成了一个简朴版的 then 要领了
加强版 then
上面的完成,模仿了 then 要领的逻辑,然则另有一些瑕玷:
- then 要领只能增添一个,比方
let p = new PromiseA(resolve => {
setTimeout(() => {
resolve(1);
}, 0);
});
p.then(value => {
console.log('then-->' + value);
}); //无输出,由于没触发到,被后一个覆蓋了
p.then(value => {
console.log('then2-->' + value);
}); ////then---> 1
- promise 没有状况,当 promsie 在增添 then 的时刻已完成了,没法获得效果
- 没有完成:假如上一个 promise 的返回值也是一个 Promise 对象时,则会比及这个 Promise resolve 的时刻才实行下一个
为了处理第一点,引入了事宜监听,简朴的完成以下:
export default class EventEmitter {
constructor() {
this._events = {};
}
on(type, fn, context = this) {
if (!this._events[type]) {
this._events[type] = [];
}
this._events[type].push([fn, context]);
}
trigger(type) {
let events = this._events[type];
if (!events) {
return;
}
let len = events.length;
let eventsCopy = [...events];
for (let i = 0; i < len; i++) {
let event = eventsCopy[i];
let [fn, context] = event;
if (fn) {
fn.apply(context, [].slice.call(arguments, 1));
}
}
}
}
所以进一步对 PromiseA 举行革新:
const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
};
const EventType = {
fulfill: 'fulfill',
reject: 'reject'
};
class PromiseA {
constructor(executor) {
//初始化事宜监听及状况
this.eventEmitter = new EventEmitter();
this.status = STATUS.PENDING;
const resolve = value => {
if (value instanceof PromiseA) {
value.then(
value => {
this.resolve(value);
},
error => {
this.reject(error);
}
);
} else {
this.resolve(value);
}
};
const reject = err => {
this.reject(err);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
resolve(value) {
//增添状况
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.resultValue = value;
this.eventEmitter.trigger(EventType.fulfill, value);
}
}
reject(error) {
//增添状况
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.resultValue = error;
this.eventEmitter.trigger(EventType.reject, error);
}
}
then(onResolve, onReject) {
//依据状况差别处置惩罚
if (this.status === STATUS.PENDING) {
return new PromiseA((resolve, reject) => {
//增添事宜监听
this.eventEmitter.on('fulfill', value => {
try {
if (onResolve) {
let newValue = onResolve(value);
resolve(newValue);
} else {
resolve(value);
}
} catch (err) {
reject(err);
}
});
//增添事宜监听
this.eventEmitter.on('reject', value => {
try {
if (onReject) {
let newValue = onReject(value);
resolve(newValue);
} else {
reject(value);
}
} catch (err) {
reject(err);
}
});
});
}
if (
this.status === STATUS.FULFILLED ||
this.status === STATUS.REJECTED
) {
return new PromiseA((resolve, reject) => {
let callback = returnValue;
if (this.status === STATUS.FULFILLED) {
callback = onResolve;
}
if (this.status === STATUS.REJECTED) {
callback = onReject;
}
try {
let newValue = callback(this.resultValue);
resolveValue(newValue, resolve, reject);
} catch (err) {
reject(err);
}
});
}
}
}
到这里,我们的 then 要领基本就完成了。
末了另有一个小知识点,就是实行机遇的题目:
setTimeout(function() {
console.log(4);
}, 0);
new Promise(function(resolve) {
console.log(1);
resolve();
}).then(function() {
console.log(3);
});
console.log(2);
//输出效果会是: 1、2、3、4
promise.then,是异步的,属于 microtask,实行机遇是本次事宜轮回完毕之前,而 setTimeout 是 macrotask,实行机遇是鄙人一次事宜轮回的最先之时
完成这个功用,我利用了第三方库 microtask 来模仿。所以 PromiseA 修改成:
resolve(value) {
microtask(() => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.resultValue = value;
this.eventEmitter.trigger(EventType.fulfill, value);
}
})
}
reject(error) {
microtask(() => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.resultValue = error;
this.eventEmitter.trigger(EventType.reject, error);
}
});
}
到此为止,我们的 then 要领已功德圆满了。最难题的一步已处理了
catch
Promise 跟一般回调的一大上风就是非常处置惩罚,我们引荐运用 Promise 的时刻,老是运用 catch 来替代 then 的第二个参数。等于:
//bad
let p = new Promise((resolve, reject) => {
//...异步操纵
}).then(
value => {
//胜利
},
() => {
//失利
}
);
//good
let p = new Promise((resolve, reject) => {
//...异步操纵
})
.then(value => {
//胜利
})
.catch(() => {
//失利
});
接下来让我们完成 catch 要领:
catch(reject) {
return this.then(null, reject);
}
哈哈~ , 你没看错。你已完成了 catch 要领
all 要领
Promise.all 是一个很好用的要领。接收一个 promise 数组,然后比及一切的异步操纵都完成了,就返回一个数组,包括对应的值
详细完成以下:
static all(promiseList = []) {
//返回promise以便链式操纵
return new PromiseA((resolve, reject) => {
let results = [];
let len = promiseList.length;
let resolveCount = 0; //用于计数
let resolver = function (index, value) {
resolveCount++;
results[index] = value;
if (resolveCount === len) {
resolve(results);
}
};
//遍历实行一切的promise
promiseList.forEach((p, i) => {
if (p instanceof PromiseA) {
p.then((value) => {
resolver(i, value);
}, (err) => {
reject(err);
})
} else {
resolver(i, p);
}
})
});
}
race 要领
race 要领为竞速,第一实行完的为准。所以只需轮回一遍实行就能够了。
当有第一个将 Promise 的状况改变成 fullfilled 或 reject 以后,其他的就都无效了
static race(promiseList = []) {
return new PromiseA((resolve, reject) => {
promiseList.forEach((p, i) => {
if (p instanceof PromiseA) {
p.then((value) => {
resolve(value);
}, (err) => {
reject(err);
})
} else {
resolve(p);
}
})
})
}
小结
我们完成了一个简朴版的 promise, 另有一些其他的要领在这里没有讲到。感兴趣的朋侪能够自行去研讨哈~
附上代码完全的完成 :
https://github.com/chen434202…