几行代码就让你彻底明白promise的原理

今天带大家了解一个神器,promisepromise所解决的问题有哪些?

  1. 解决回调地狱(多个ajax嵌套);
  2. 解决并发异步(同一时刻内获取并发的结果);
  3. 链式调用。

1. 什么是promise

promise 分为3种状态,分别是:

  • pendding:初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

2.promise的使用

promise是一个类,内置的类,使用方式是new Promise(),里面有一个参数executor,executor有两个参数resolve,rejected,promise必须有方法then,then有两个参数onFulfilled, onRejected。

2.1 参数

let p = new Promise((resolve,reject)=>{
  //有一个参数叫executor,executor有两个参数resolve,对应的状态fulfilled操作成功完成,rejected操作失败
});
p.then(onFulfilled, onRejected){
    //有两个参数onFulfilled, onRejected
}
复制代码

2.2 promise的基本使用方法

2.2.1情况1:
let p = new Promise((resolve, reject)=> {
    resolve('林俊杰');
});
p.then(data=>{
    console.log(data);//'林俊杰'
},err=>{
    console.log(err);
})
复制代码
  • 上面例子走的是成功时的内容,而输出的data是resolve的参数
2.2.2情况2:
let p = new Promise((resolve, reject)=> {
    reject('周杰伦');
    resolve('林俊杰');
});
p.then(data=>{
    console.log(data);
},err=>{
    console.log(err);//'周杰伦'
})
复制代码
  • 上面例子走到失败的内容,同样输出的是reject的参数,不同的是promise中同时存在reject();resolve();时,走的是放在前面的一个状态,只要走了失败就不会走成功(只要走了成功就不会走失败)
2.2.3情况3
let p = new Promise((resolve, reject)=> {
    throw new Error('林俊杰');
});
p.then(data=>{
    console.log('d',data);
},err=>{
    console.log('e',err);//e Error: 林俊杰
})
复制代码
  • 上面例子,抛出错误时,同样走到了reject,所以当抛错时也会走到reject,并不会报错。。。(模拟时可以使用try...catch)

接下来是模拟手写的promise(根据promise a+)

class MyPromise {
  constructor(executor) {//参数executor放在constructor中
    // 默认状态是等待态
    this.status = 'pending';
    this.value = undefined;//成功的参数
    this.reason = undefined;//失败的参数
    let resolve = (data) => {//注意this,最好用es6箭头函数
      if (this.status === 'pending') {//只有当状态是pending时才会执行,下同
        this.value = data;
        this.status = 'fulfilled';
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
      }
    }
    try { // 执行时可能会发生异常eg:throw new Error();
      executor(resolve, reject);//executor是promise参数,executor有两个自己的参数
    } catch (e) {
      reject(e); // promise失败了
    }
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
  }
}
module.exports = Promise;
复制代码
  • 上面是最简单的一个实现,那么问题来了,如果,在我的promise中有一个异步的,比如定时器
let p = new Promise((resolve, reject)=> {
    setTimeout(()=>{
        reject('林俊杰');
      },1000)
});
p.then(data=>{
    console.log('d',data);
},err=>{
    console.log('e',err);//e : 林俊杰
})
复制代码
  • 内置的promise会等到定时器1s执行完以后再执行 then,而此时我们自己完成的MyPromise是不会输出任何,原因在于,then要先于异步的定时器执行,而此时的then的状态是pending,所以不会走then的任何状态。所以我们需要把then的成功的函数与失败的函数分别用数组存储起来。onResolvedCallbacks=[],onRejectedCallbacks=[],放在实例上,所以当状态是pending时,存起来
if(this.status === "pending") {
    //相当于发布订阅
    this.onResolvedCallbacks.push(()=>{
        onFulFilled(this.value);
    })
    this.onResolvedCallbacks.push(()=>{
        onRejected(this.reason);
    })
}
复制代码
  • 相当于发布订阅模式,只有以下状态改变的时候调用
let resolve = (data)=>{
    if(this.status === 'pending') {
        this.value = data;
        this.status = 'fulfilled';
        this.onResolvedCallbacks.forEach(fn=>fn());
    } 
}
let reject = (err)=>{
    if(this.status === 'pending') {
        this.reason = err;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn=>fn());
    }
}
复制代码
  • 以上是基本的实现,那最主要的还是promise的链式调用。

2.3 promise的链式调用方法

let p = new Promise((resolve, reject) => {
  resolve();
});
p.then((data) => {
  // return new Promise((resolve,reject)=>{
  //   resolve(new Promise((resolve,reject)=>{
  //     reject('1234565');
  //   }))
  // })//返回值是promise,会取promise的返回结果作为外层下一次then的参数
  return 123;//返回值是普通值,直接把值作为外层下一次then的参数
  return new Error;//返回的是普通值
}, err => {
  console.log('p1:err', err);
}).then((data)=> {
  console.log('d',data);
},err=>{
  console.log('e',err);
})
复制代码
  • then的返回值如果是promise,会取promise的返回结果作为外层下一次then的参数,如果返回的是普通值,直接把值作为外层下一次then的参数
  • 那我们接着往下写吧我们看到的是
let p = new Promise((resolve, reject) => {
    resolve('林俊杰');
});
p.then().then()
p.then();
复制代码
  • 上面例子的两个then方法是不一样的结果,所以then方法调用后,返回的是新的promise(也有可能不是promise),链式写法靠的是返回新的promise(而不是this),有可能是失败走向成功的,所以then里面要return新的promise2
  • 此时主要处理promise2(简称p2),与其返回值X的关系,方法resolvePromise(promise2, x, resolve, reject);
2.3.1 p2的返回值x也是p2,如下:
let promise2 = p.then(()=>{
    return promise2;//Chaining cycle detected for promise,会报错循环引用。
})
复制代码
  • 上面例子中,p2会等着自己的返回结果,一直没有结果不会调成功也不会调用失败,会报循环引用错误
2.3.2 p2的返回值x有可能是普通值,如下:
let promise2 = p.then((data)=>{
    return '林俊杰';
},(err)=>{
    return 'z'
})
promise2.then((data) => { 
    console.log('d', data); //d 林俊杰
}, (err) => { 
    console.log('e', err) 
});
复制代码
  • 上面例子,p2的返回值如果是普通值(只要不抛出错误throw new Error()),不管执行的是成功函数(onFulfilled)还是失败函数(onRejected),都会走到下一个then的成功态里
2.3.3 p2的返回值x有可能是函数,那么认为它是promise,如下:
let p2 =p.then((data) => {
  return new Promise((resolve, reject)=>{
    resolve(new Promise((resolve, reject)=>{
      resolve(1111)
    }))
  })
 }, (err) => {}).then((data) => {
  console.log('d', data);//d 1111
}, err => {
  console.log('e', err);
})
复制代码
  • 上面例子,p2的返回值是多层promise嵌套,需要递归调用resolvePromise方法。
  • 接下来根据promise a+来写方法resolvePromise
function resolvePromise(promise2, x, resolve, reject) {
    //promise2返回新的promise,x为promise2的返回值,另外两个是参数
  // 判断返回值是不是promise2 本身
  if (promise2 === x) {//报错,见2.3.1
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // x不是null或者是对象或者函数(当然null的话是返回null,同普通值)
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called; // 就是为了防止,调取了成功态,在去调用失败态,
               //或者调取了失败态,在去调取成功态,弄一个标识
    try { // 防止取then时出现异常 例如Object.defineProperty,修改了then方法
      let then = x.then; // 取x的then方法 {then:{}}
      if (typeof then === 'function') { // 如果then是函数我就认为它是promise
        // call:this指向x,后面分别是成功的回调和失败的回调
        then.call(x, y => { // 如果y是promise就继续递归解析promise
          if(called) return;
          called = true;
          resolvePromise(promise2,y,resolve,reject);
        }, r => { // 只要失败了就失败了
          if (called) return;
          called = true;
          reject(r);
        });
      }else{ // then是一个普通对象,就直接成功即可
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else { 
    resolve(x); // x就是一个普通值,例子见2.3.2
  }
}
复制代码

2.4 promise值的穿透

let p = new Promise((resolve, reject) => {
    resolve('林俊杰');
});
p.then().then().then((data)=>{
    console.log(data);//林俊杰
})
复制代码
  • 上面例子,前面两个then都没有传参数或者参数为null,但是仍然将数据传给了最后一个then的onFulfilled成功态或者onRejected失败态,所以在then方法中首先要判断一下参数
// 解决onFulFilled,onRejected没有传的问题
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
复制代码

对于原生的promise的then方法来说是属于异步的,会先执行同步代码以后再去执行then,例如:

p.then(()=>{console.log(1});
console.log(2);
复制代码
  • 上面例子中可以得到的是2比1要先输出,所以then是异步,所以每一个执行都加一个定时器包起来

2.5 promise的其他方法

2.5.1 catch

p.then().then().catch((err)=>{
console.log(err)
}).then((data)=>{
    console.log(data)
})
复制代码

其实catch是then的一个简写的方法,没有传成功的回调的时候走的是catch,但是catch走完了,还会继续往下走then,所以返回的依旧是promise。

catch(rejected){
    return this.then(null, rejected)
}
复制代码

2.5.2 resolve + reject

Promise.resolve = function(val) {
    return new Promise((resolve, reject)=> resolve(val))
}
Promise.reject = function(val){
    return new Promise((resolve, reject)=> reject(val))
}
复制代码

2.5.3 all

原生上的all方法,以数组的形式把两个promise

p.all([promise1,promise2]).then((arr)=>{
    consolr.log(arr)
})
复制代码
  • 其中arr是all里面promise执行完的内容,顺序依次,全成功才成功,一个失败就失败
Promise.all = function(promises) {
    return new Promise((resolve,reject)=> {
    let arr = [];
    let i = 0;
    function processData(index, data) {
        arr[index] = data;
        i++;
        if(i === promises.length) {
            resolve(arr);
        }
    }
        for(let i =0;i<promises.length;i++){
            promises[i].then(data=>{
               processData(i,data); 
            },reject);
        }
    })
}
复制代码

2.5.4 race

race方法是选择多个promise首先执行完成的哪一个promise的执行结果返回即可。一个成功即成功

Promise.race(promises) {
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<promises.length;i++){
            promises[i].then(resolve,reject)
        }
    }) 
}
复制代码

2.6 bluebird

  • bluebird是让异步方法直接promise化,使用方式
npm install bluebird
复制代码
let read = bluebird.promisify(fs.readFile);
read('./a.txt', 'utf8').then(data=>{
    console.log(data);
})
复制代码
  • 使异步方法fs.readFilepromise化。相当于包了一层promise
function promisify(fn) {
    return function(...args){
        return new Promise((resolve, reject)=>{
            fn(...args,function (err,data) {
                if(err) reject(err);
                resolve(data);
            })
        })
    }
}
复制代码
let r = blusebird.promisifyAll(fs);
复制代码
  • 这个方法把fs上面的方法都增加了一个Async,并且promise化。

2.7 Q

  • 同样可以promise化
npm install q
复制代码
let Q = require('q');
let defer = Q.defer()
复制代码
  • 这里可以生成promise语法糖defer。

  • 下一篇来解await async。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值