1.promise 是什么鬼?
(小姐姐表示之前在项目中用promise时只是机械的照抄,完全不懂原理)
为什么会出现promise呢?
promise是异步编程的一种解决方案。
promise主要用来解决两个问题:1.回调地域 2.可以支持多个并发的请求,获取并发请求中的数据
另外promise 有三种状态 pending->等待态 resolve->成功态 reject->失败态 状态一旦改变就不会再变
接下来 我们就深度解析下promise(小板凳摆好哟!!)
先new一个promise来看看
let p =new Promise1((resolve,reject)=>{
setTimeout(()=>{
resolve('成功');
},1000)
})
p.then(data=>{
return new Promise((resolve,reject)=>{
resolve(1000)
})
},err=>{
console.log(err)
}).then(data=>{
console.log(data);
})
复制代码
从这个例子中我们可以看到:
- .new Promise时需要传递一个executor执行器,执行器会立刻执行
- .执行器中传递了两个参数,resolve成功的函数(或者reject失败的函数) 他调用时可以传一个值 值可以是任何值
- 只能从pending态到成功或者失败
- promise 实例 每个实例都有一个then方法 这个方法传递两个参数 一个是成功一个是失败
- 如果调用then时 发现已经成功了 会让成功函数执行并且把成功的内容当作参数传递到函数中 了解了这些就让我们动起来吧,啦啦啦啦啦......
2.根据promiseA+实现一个自己的promise
上面已经说了promise是一个构造函数,写起来...
function Promise(executor){
let self=this;
self.status='pending';
self.value=undefined; //成功回调的值
self.reason=undefined;//失败的原因
self.onResolved=[]; //存放成功的回调 (异步操作时需要先将成功/失败的结果先存起来)
self.onRejected=[]; //存放失败的回调
function resolve(value){ //成功的回调
if(self.status=='pending'){
self.value=value;
self.status='resolved';
self.onResolved.forEach(fn=>fn());
}
}
function reject(reason){ //失败的回调
if(self.status=='pending'){
self.reason=reason;
self.status='rejected';
self.onRejected.forEach(fn=>fn());
}
}
try {
executor(resolve,reject) // 执行器执行时需要捕获异常
} catch (e) {
reject(e)
}
}
复制代码
构造函数写好了,接下来then方法链式调用
function resolvePromise(promise2,x,resolve,reject){
if(promise2===x){
return reject(new TypeError('循环引用'));
}
//这个方法是处理所有promise的实现
let called; //用来防止多次调用
// x是除了null以外的对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try{
let then = x.then;
if(typeof then ==='function'){
//让返回的x也就是返回的promise执行 用它的状态让promise2成功或者失败
//因为这里还涉及到别人写的promise 有的人写的promise 会成功还会调用失败
then.call(x, (y)=>{ //如果Y是promise就继续递归promise
//递归解析 如果resolve返回的是一个promise就要不停的让resolve的结果进行处理
if (called) return ;
called=true;
resolvePromise(promise2, y, resolve, reject);
},(e)=>{
if (called) return ;
called=true;
reject(e) //只要失败了就失败了
});
}else {
resolve(x)
}
}catch(e){
if (called) return ;
called=true;
reject(e)
}
}else {
resolve(x) //x就是一个普通值 (就用这个值让返回的promise2成功即可)
}
}
Promise.prototype.then = function (onfulfilled, onrejected) {
// onfulfilled / onrejected是一个可选的参数
onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val=>val;
onrejected = typeof onrejected === 'function' ? onrejected :err => {
throw err;
}
let self = this;
// 需要判断onfulfilled/onrejected执行的结果和promise2的关系
//
let promise2;
promise2 = new Promise((resolve, reject) => {
if (self.status === 'resolved') {
setTimeout(() => {
try {
let x = onfulfilled(self.value); //如果成功 返回成功的回调
resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
} catch (e) {
reject(e);
}
}, 0);
}
if (self.status === 'rejected') {
setTimeout(() => {
try {
let x = onrejected(self.reason); //如果失败 返回失败的回调
resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
} catch (e) {
reject(e);
}
}, 0);
}
if (self.status === 'pending') { //如果一直是pending态需要先把成功/失败的回调存起来等到成功或者失败状态时再执行
self.onResolved.push(function () {
setTimeout(() => {
try {
let x = onfulfilled(self.value);
resolvePromise(promise2, x, resolve, reject); // 解析x(成功/失败的返回值) 和 promise2的关系
} catch (e) {
reject(e);
}
}, 0);
});
self.onRejected.push(function () {
setTimeout(() => {
try {
let x = onrejected(self.reason);
resolvePromise(promise2, x, resolve, reject); // 解析x 和 promise2的关系
} catch (e) {
reject(e);
}
}, 0);
});
}
})
return promise2;
}
复制代码
这一段有点儿长,额。。。。
- 调用then时会传入两个参数(可选)成功的回调跟失败的回调 then方法想要链式下去那么得返回一个promise才可以继续链式
- promise中可以同一个实例then多次,如果是pending状态 需要把成功或者失败的结果先存起来 等到状态确定后 再依次将对应的函数执行
- then方法resolve返回的值如果是普通值,那么直接传递,如果resolve返回的是一个promise就要不停的让resolve的结果进行处理
核心点就是这些,代码中也加了注释,不知道大家懂了没,感觉有点儿乱
说下catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调。
所以很简单咯
Promise.prototype.catch=function(onrejected){
return this.then(null,onrejected)
}
复制代码
实际应用就是这样啦
let p =new Promise((resolve,reject)=>{
resolve(100);
})
//catch就是不写成功的回调的then方法
p.then(data=>{
console.log(data);
}).then(data=>{
throw Error()
}).catch(err=>{
console.log(err)
})
复制代码
接下来说all方法
all方法的参数是一个数组 会按照数组的结果放到成功的回调里(只有全成功才成功)
Promise.all = function (promises) {
//promises是一个promise的数组
return new Promise(function (resolve, reject) {
let arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, data) {
arr[index] = data;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
processData(i, data)
}, reject)
}
})
}
复制代码
实例写起来
Promise.all([fs.readFile('./atxt','utf8'),fs.readFile('./b.txt','utf8')]).then((data)=>{
console.log(data)
})
复制代码
再接下来是race方法
race方法参数也是一个数组 会同时发起并发,但是以返回最快的结果为结果
Promise.race=function(promises){
return new Promise((resolve,reject)=>{
for(let i =0;i<promises.length;i++){
let p=promises[i];
p.then(resolve,reject)
}
})
}
复制代码
实例写起来
Promise.race([fs.readFile('./atxt','utf8'),fs.readFile('./b.txt','utf8')]).then((data)=>{
console.log(data)
})
复制代码
写到这儿,我表示已经很累了。。。。
接着搬砖
promise的resolve reject方法
这个就很简单了 resolve方法 就是直接掉成功的回到 reject方法就是直接调失败的回调
Promise.resolve=function(value){
return new Promise((resolve,reject)=>{
resolve(value)
})
}
Promise.reject=function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
复制代码
最后来一个测试代码是否符合a+ 规范
Promise.defer=Promise.deferred = function(){
let dfd = {};
dfd.promise=new Promise((resolve,reject)=>{
dfd.resolve=resolve;
dfd.reject=reject
})
return dfd;
}
复制代码
这段代码是promise的语法糖 可以很简单的将一个方法包装成promise实例
let Promise =require('./promise');
let fs=require('fs');
function read(url){
//语法糖
let dfd=Promise.defer();
fs.readFile(url,'utf8',(err,data)=>{
if(err) dfd.reject(err);
dfd.resolve(data)
})
return dfd.promise;
}
read('./a.txt').then(data=>{
console.log(data)
})
复制代码
如果想知道自己写的代码是否符合a+规范可以用一个插件来测试
npm install promises-aplus-tests -g
promises-aplus-tests 文件名 可以测试
复制代码
到此就写完了,回过头来发现大堆的代码 没办法表述真的太难
推荐几个实现promise的库吧 Q bluebird then 有兴趣的了解下吧
我要回家睡觉去了!!!!!!