随便说说Promise

为啥要说 promise ?

因为这是前端必须要掌握的一个知识,吹逼必备

首先说说 Promise 是什么?

Promise 是JavaScript的第一个异步标准模型,一个包含传递信息与状态的对象,emmm...它的英语意思是承诺.
so 它拥有的特点也有承诺的意思(兄弟们,拿好笔记本,划重点):

  1. 对象的状态不受外界影响,Promise 表示一个异步的操作,它有三个状态 Pending (进行时)、Reslove (已完成)、Rejected (失败),只有异步操作的结果,可以决定当前是哪一种状态
  2. 一旦状态改变就不会再接受改变,任何时候都可以得到这个结果。 Promise 对象的状态改变,只有两种可能,从 Pending 变为 Resolved 或者 Rejected。 只要这两种情况发生,状态就不会再改变了,就算状态改变了,你再对 Promise 对象添加回调,也会立即得到结果,这和事件 event 不同,事件的特点是一点你错过了它,再去监听也不会得到结果的

那 Promise 为什么会出现呢?

Promise 之前的异步

Promise之前的时候,我们用三个手段解决异步问题

  1. 回调函数
  2. 观察者模式(又称发布订阅模式)
  3. 事件机制

回调函数是最简单的异步方式,它比较容易理解和部署,也是我们最常使用的,但是缺点也很明显,不利于代码的阅读和维护,流程混乱。

观察者模式和事件机制不多说,就是适用于多次同类型的异步,不适用一次性的异步

而 Promise 可以将异步操作以同步操作的流程表达出来,

  1. 避免了层层回调函数。
  2. 如果使用回调函数, 你并不能知道对方是否执行你的回调函数,或太晚太早执行
  3. 错误也是异步,会隐式传递给reject

老哥,说了这么多,怎么用啊?

先来个最简单的Prmise

const p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('你好,兄弟') //设置成resolve状态 即是成功
    },1e3)
})
p.then(res=>{
    console.log(res) // 1s后输出 ==> 你好,兄弟
})

是不是看起来 so easy ?

下面我们来实现一下最简单的 Promise。 Promise只是一个普通的构造函数,用es3的语法也可以实现
所以在ie6都可以用,不过我们就使用class来实现了

class myPromise {
  constructor(fn) {
    this.state = 'PENDING';
    this.message = '';
    this.fns = [];
    fn(this.resolve.bind(this), this.reject.bind(this));
  }
  resolve(res) {
    this.state = 'FULFILLED';
    this.message = res;
    this._onChange();
  }
  reject(err) {
    this.state = 'REJECTED';
    this.message = err;
    this._onChange();
  }
  then(onResolved, onRejected) {
    let self = this;
    return new myPromise((resolve, reject) => {
      self.fns.push([onResolved, onRejected, resolve, reject]);
      self._onChange();
    });
  }
  _onChange() {
    this.state !== 'PENDING' &&
      this.fns.forEach(v => {
        this._change(v);
      });
  }
  _change(arr) {
    let onResolved = arr[0],
      onRejected = arr[1],
      resolve = arr[2],
      reject = arr[3];
    switch (this.state) {
      case 'FULFILLED':
        if (typeof onResolved === 'function') {
          resolve(onResolved.call(null, this.message));
        } else {
          resolve(this.message);
        }
        break;
      case 'REJECTED':
        if (typeof onRejected === 'function') {
          resolve(onRejected.call(null, this.message));
        } else {
          reject(this.message);
        }
        break;
      default:
        break;
    }
  }
}

//老哥们注意,这只是我自己写的一个简略的 Promise , 并不是真正的 Promise 的实现

const p = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('你好,兄弟'); //1s后设置成resolve状态 即是成功
  }, 1e3);
});
p.then(res => {
  console.log(res); // 1s后输出 ==> 你好,兄弟
  return '1';
})
  .then(res => {
    console.log(res); // 1s后输出 ==> '1'
  })
  .then(res => {
    console.log(res); // 1s后输出 ==> undefined
  });

Promise.resolve / reject

Promise 还提供一个快速实例化 Promise ,可以传入参数并且马上触发 Promise 链的执行

Promise.resolve = data =>
  new Promise((resolve, reject) => {
    resolve(data);
  });
Promise.reject = err =>
  new Promise((resolve, reject) => {
    reject(err);
  });

Promise.all / race

Promise 还提供 all / race 用来处理数据的并发和竞争,要求是传一个 Promise 数组作为参数,然后返回一个新的 Promise

all 是全部处理后再统一处理

Promise.all = arr =>
  new Promise((resolve, reject) => {
    let c = 0,
      l = arr.length,
      result = [];
    l === 0 && resolve(result);
    arr.forEach(pro => {
      Promise.resolve(pro).then(res => {
        result.push(res);
        c++;
        c === l && resolve(result);
      },
        err => {
          reject(err);
        }
      );
    });
  });

race 则是谁异步时间短谁就会被处理

Promise.race = arr =>
  new Promise((resolve, reject) => {
    arr.forEach(pro => {
      Promise.resolve(pro).then(resolve, reject);
    });
  });

Promise.then / catch

Promise 的 then 函数我们也知道是怎么用的,它始终以当前的结果返回一个新的Promise
而之前我们说到 Promise 的错误是异步的 也直接调用到 reject,
那我们在 resolve 或者 reject 出现错误应该怎么做呢? Promise 还提供一个catch 用来捕捉错误

Promise.catch = reject => this.then(undefined,reject)

怎么用呢?

const p = new Promise((resolve, reject) => {
  //执行操作
  //..略
});

p.then(resolve, reject)
  .then(resolve, reject)
  .then(resolve, reject)
  .catch(err => {
    //如果以上出现错误会直接传递到此处执行错误处理
  });

Promise 和 setTimeout

说是异步,那么和我们最原始的 setTimeout 有什么区别呢?
先让我们看一道面试题目

console.log('a');

setTimeout(() => {
  console.log('b');
});

console.log('c');

new Promise((resolve, reject) => {
  console.log('d');
  resolve('e');
}).then(res => {
  console.log(res);
});

console.log('f');

输出顺序是什么呢 ?

a => c => d => f => e => b

这里就要关系到js的 EventLoop 机制了,这里并不细说,只是抛个引子,有兴趣的朋友可以去看看相关文章

结尾

到这里就结束了,老哥们,要去搬砖了

如果发现文章有什么问题,可以留言哦

posted @ 2018-07-08 15:06  云剪者  阅读(445)  评论(3编辑  收藏