# 手写实现Promise
## Promise异步编程
异步编程简介:无论在浏览器环境还是node环境都需要JavaScript的异步编程,如在浏览器环境中的定时器、事件、ajax等或是node环境中的文件读取、事件等。伴随着异步编程就有回调机制,异步编程免不了回调。
异步编程问题:产生回调地狱,难于维护和扩展。
try、catch只能捕捉同步代码中出现的异常。
同步并发的异步操作存在一定的问题。
解决方案: ES6 Promise可以解决回调地狱,以及同步并发的异步问题。
jQuery的Callbacks和Lodash的after都是解决回调问题的其他方法
### Promise使用
```js
//excutor function 同步执行
let promise = new Promise((resolve,reject)=>{
//异步操作
setTimeout(()=>{
Math.random()*100 > 60 ? resolve("ok"): reject("fail");
},1000);
});
//Promise内部状态,pending(等待),onFulFilled(成功),onReject(失败)
//注册回调,异步执行
//宏任务(setTimeout) 微任务
//微任务有优先执行权
//then 可以链式操作
//上一个then不抛出错误的话,下一个then会执行成功函数
//返回值作为下一个then注册函数的执行参数
//如果返回值为Promise对象,则下一个then的执行取决于该对象的执行函数
promise.then((val)=>{//微任务
console.log(val);
return new Promise((resolve,reject)=>{
reject("newPromise:fail");
});
},(reason)=>{
console.log(reason);
return "fail then1:param";
}).then((val)=>{
console.log("ok then2:",val);
},(reason)=>{
console.log("fail then2:",reason);
});
```
#### then 注册回调返回值
#### catch 异常捕获
```js
let promise = new Promise((resolve,reject)=>{
throw new Error("test error");
});
//失败函数捕获
promise.then(null,(reason)=>{
console.log(reason);
});
//链式调用时如果有空then,则相当于不存在可忽视
//catch捕获
//catch后面可以继续链式调用
promise.then().catch((error)=>{
console.log(error);
}).then((val)=>{
console.log(val,"after catch: ok");
},(reason)=>{
console.log(reason,"after ctach: fail");
})
```
#### finally 最后处理函数
#### Promise.all 同步并发异步的结果
```js
let oPro = new Promise(()=>{});
//Promise.all参数为数组,数组元素必须为Promise对象,其会将
//多个Promise实例包装成一个新的Promise实例。
//全部成功时数组内元素的返回值组成数组,只要有失败时返回最先被reject失败
//状态的值
Promise.all([oPro,oPro,oPro]).then((val)=>{
console.log(val);//val为数组
});
```
#### Promise.race 谁先成功处理谁
```js
let oPro = new Promise(()=>{});
//Promise.race([p1,p2,p3]);里面的哪个结果获得的快,就返回那个结果,
//不管结果本身成功或失败。谁的状态先发生改变就返回谁的状态
Promise.race([oPro,oPro,oPro]).then((val)=>{
console.log(val);
},(reason)=>{
console.log(reason);
});
```
### Promise模拟实现
点击查看 [Promise规范][promise-standard]
```js
//考虑兼容性,用ES5实现
function MyPromise(excutor) {
this.status = "pending";
this.resolveValue = null;
this.rejectReason = null;
this.resolveCallbackList = [];
this.rejectCallbackList = [];
try {
excutor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
MyPromise.prototype = {
resolve(val) {
if (this.status === "pending") {
this.status = "FulFilled";
this.resolveValue = val;
this.resolveCallbackList.forEach(function (cbFn) {
cbFn();
});
}
},
reject(reason) {
if (this.status === "pending") {
this.status = "Rejected";
this.rejectReason = reason;
this.rejectCallbackList.forEach(function (cbFn) {
cbFn();
});
}
},
then(onFulFilled, onRejected) {
var self = this;
self._dealNullThen(onFulFilled)._dealNullThen(onRejected);
return new MyPromise(function (resolve, reject) {
if (self.status === "FulFilled") {
//模拟异步执行,此为宏任务,底层代码为微任务
setTimeout(function () {
try {
var nextResolveValue = onFulFilled(self.resolveValue);
self._dealReturnValPromse(nextResolveValue, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else if (self.status === "Rejected") {
setTimeout(function () {
try {
var nextRejectValue = onRejected(self.rejectReason);
self._dealReturnValPromse(nextRejectValue, resolve, reject, true);
} catch (e) {
reject(e);
}
}, 0);
} else if (self.status === "pending") {
self.resolveCallbackList.push(function () {
setTimeout(function () {
try {
var nextResolveValue = onFulFilled(self.resolveValue);
self._dealReturnValPromse(nextResolveValue, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
self.rejectCallbackList.push(function () {
setTimeout(function () {
try {
var nextRejectValue = onRejected(self.rejectReason);
self._dealReturnValPromse(nextRejectValue, resolve, reject, true);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
},
_dealNullThen(fn) { //处理空then情况
if (!fn) {
fn = function (val) {
return val;
}
}
return this;
},
_dealReturnValPromse(returnVal, resolve, reject, isRejected) {
if (returnVal instanceof MyPromise) {
//若返回值为MyPromise对象,则后面的执行状态由该对象来决定
returnVal.then(function (val) {
resolve(val);
}, function (reason) {
reject(reason);
});
} else {
//如果返回值不为MyPromise对象,则执行回调函数
if (!isRejected) {
resolve(returnVal);
} else {
reject(returnVal);
}
}
},
};
MyPromise.race = function (promiseArr) {
return new MyPromise(function (resolve, reject) {
promiseArr.forEach(function (ele) {
ele.then(resolve, reject);
});
});
};
//全部成功才执行成功回调,只要有一个失败就执行失败回调
MyPromise.all = function (promiseArr) {
return new MyPromise(function (resolve, reject) {
var returnValueArr = [],
count = 0;
for (var i = 0, len = promiseArr.length; i < len; i++) {
(function (i) {
promiseArr[i].then(function (val) {
returnValueArr[i] = val;
if (++count == len) {
resolve(returnValueArr);
}
}, function (reason) {
reject(reason);
});
}(i));
}
});
};
```
## ES6 Symbol
数据结构:第七种数据结构Symbol
特点:唯一,可作为对象的属性,有静态属性Symbol.iterator
## ES6 Iterator
**迭代器目的**:标准化迭代操作。
**迭代模式**:提供一种方法可以顺序获得聚合对象中的各个元素,是一种最简单也是最常见的设计模式。它可以让用户透过特定的接口巡访集合中的每一个元素而不用了解底层的实现。
**迭代器简介**:依照与迭代模式的思想而实现,分内部迭代器和外部迭代器。
**内部迭代器**:本身是函数,该函数内部定义好迭代规则,完全接受整个迭代过程,外部只需要一次初始调用。
Array.prototype.forEach、jQuery.each内部迭代器
**外部迭代器**:本身是函数,执行返回迭代对象,迭代下一个元素必须显式调用,调用复杂度增加,但灵活性增强。
function outerIterator(){}外部迭代器
```js
//模拟写自己外部迭代器
function OuterIterator(o){
let curIndex=0;
let next=()=>{
return {
value:o[curIndex],
done:o.length == ++curIndex,
}
}
return {
next,
}
}
let arr=[1,2,3];
let oIt=outerIterator(arr);
oIt.next();
oIt.next();
oIt.next();
```
### 部署Iterator
```js
let obj={
0:"a",
1:"b",
2:"c",
length:3,
//要能迭代,必须部署Iterator,符合ES6
[Symbol.iterator]:function (){
let curIndex=0;
let next = () => {
return {
value: this[curIndex],
done: this.length == curIndex++,
}
};
return{
next,
}
},
}
console.log([...obj]);
```
### Generator
Generator简介:生成器,本身为函数,执行后返回迭代对象,函数内部要配合yield使用,Generator函数分段执行,遇到yield即暂停。
特点:
function和函数名之间需要带*
函数体内yield表达式,产出不同的内部状态(值)
```js
//示例 Generator产生迭代对象
function *test(){
let val1= yield "a";
console.log(val1);//val1的值为第二次next中传入的值
yield "b";
yield "c";
return "d";
}
let oG=test();
oG.next();//{value:"a",done:false}
oG.next();//{value:"b",done:false}
oG.next();//{value:"c",done:false}
oG.next();//{value:"d",done:true}
```
改造前面的代码
```js
let obj={
0:"a",
1:"b",
2:"c",
length:3,
//要能迭代,必须部署Iterator,符合ES6
[Symbol.iterator]:function (){
let curIndex=0;
while(curIndex != this.length){
yield this[curIndex++];
};
},
}
console.log([...obj]);
```
Generator函数使用
```js
function *read(path){
let val1 = yield readFile(path);
let val2 = yield readFile(val1);
let val3 = yield readFile(val2);
return val3;
}
let oG = read();
let {value, done} = oG.next();
value.then((val)=>{
let {value, done} = oG.next();
value.then((val)=>{
let {value, done} = oG.next();
value.then((val)=>{
console.log(val);
});
});
});
//递归优化
function Co(oIterator){
return new Promise((res,rej)=>{
let next = (data)=>{
let {value, done} = oIterator.next(data);
if(done){
res(value);
}else{
value.then((val)=>{
next(val);
},rej);
}
};
next();
});
}
//使用
Co(read()).then((val)=>{
console.log(val);
});
```
#### Promise化
```js
let fs = require("fs");
let path="./data.txt";
let format="utf-8";
//原始函数
function readFile(){
return new Promise((res,rej)=>{
fs.readFile(path,format,(err,data)=>{
if(err){
rej(err);
}else{
res(data);
}
});
});
}
//对函数进行promise化 (npm i bluebird)
function promisify(fn){
return (...arg)=>{
return new Promise((res,rej)=>{
fn(...arg,(err,data)=>{
if(err){
rej(err);
}else{
res(data);
}
});
});
};
}
let readFilePromisify=promisify(fs.readFile);
readFilePromisify(path,format).then((val)=>{
console.log(val);
});
//进一步对对象内异步方法进行promise化
function promisifyAll(){
for(let key in obj){
let fn=obj[key];
if(typeof fn === "function"){
obj[key + "Async"] = promisify(fn);
}
}
}
promisifyAll(fs);
fs.readFileAsync(path,format).then((val)=>{
console.log(val);
});
```
### async & await
async简介:async函数,是Generator语法糖,通过babel编译后可以看出它就是Generator+Promise+Co(递归)思想实现的,配合await使用。
目的:优雅的解决异步操作问题。
```js
//解决回调地狱
//try catch
//同步并发的异步结果
async function read(path){
try{
let val1 = await readFile(path);
let val2 = await readFile(val1);
let val3 = await readFile(val2);
}catch(e){
console.log(e);//能够捕获异常
}
return val3;
}
read(path).then((val)=>{
console.log(val);
});
//解决同步并发的异步问题
//Promise.all有局限性,一个异常其他也不能出结果
Promise.all([readFile(path1),readFile(path2),readFile(path3)])
.then((val)=>{
console.log(val);
},(reason)=>{
console.log(reason);
});
//使用async和await可以解决
```