JavaScript接化发:Promise

189 阅读5分钟

什么是Promise?

  • 异步编程的一种解决方案,ES6新特性
  • 从语法上看,是一个对象,可以获取异步操作的消息
  • 从本意上看,是一个承诺,一段时间后给你结果
  • 三种状态:pending,fulfilled,rejected

为什么需要Promise?

  • 解决回调地狱(第一个函数的输出是第二个函数的输入)
  • 支持多个并发的请求,获取并发请求中的数据
  • 解决异步的问题,本身不能说Promise是异步的

Promise用法

构造函数

让我们new一个Promise构造函数:

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('successfully');
    resolve('successfully');
  }, 1000);
});
  • resolve:异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

then方法

我们可以从表面上发现Promise能简化代码的写法,然而本质上,Promise的精髓在于用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活,让我们看一下Promise的应用场景:

promise.then((data) => {
  console.log(data);
}).then((data) => {
  console.log(data);
}).then((data) => {
  console.log(data);
});

reject方法

把状态设置为rejected,这样我们在then中就能捕获到,然后执行失败状态下的回调,then中传了两个参数,第一个对应resolve的回调,第二个对应reject的回调:

let promise = new Promise((resolve, rejected) = {
  setTimeout(function(){
    var num = Math.cell(Math.random()*10);
    if (num <= 5) {
      resolve(num);
    } else {
      reject('failed');
    }
  }, 1000);
});
promise.then((data) => {
  console.log('resolved', data);
}, (err) => {
  console.log('rejected', err);
});

catch方法

我们再来看看Promise的catch,它和then的第二个参数一样,用来指定reject的回调,效果和写在then的第二个参数里面一样,不过还有一个作用,那就是在执行resolve的回调时如果抛出了异常,那么不会报错,而是会进入到catch中:

promise.then((data) => {
  console.log('resolved', data);
  doSomeThingWrong();
}).catch((err) => {
  console.log('rejected', err);
})

all方法

Promise还有一个骚方法,那就是all,它会接收一个Promise数组参数,在所有数组内Promise的异步操作执行完毕后才执行自己的回调,让我们可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据:

let promise1 = new Promise(function(resolve, reject) {});
let promise2 = new Promise(function(resolve, reject) {});
let promise3 = new Promise(function(resolve, reject) {});

let promise4 = Promise.all([promise1, promise2, promise3]);
promise4.then(function() {
  // promise1, promise2, promise3都成功
}, function() {
  // promise1, promise2, promise3中有任何失败
});

race方法

有的时候我们可能需要给某个异步请求设置超时时间来避免我们一直等待,所有我们还会用到Promise的race,它的作用是比较两个请求谁更快,谁快执行谁的回调函数:

// 请求某张图片的资源
function requestImg() {
  var promise = new Promise((resolve, reject) => {
    var img = new Image();
    img.onload = function() {
      resolve(img);
    }
    img.src = 'link';
  });
  return promise;
}

// 请求及时的延时函数
function timeout() {
  var promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('timeout');
    }, 10000);
  });
  return promise;
}

// 如果时间内requestImg执行成功了,就进入then
// 如果时间内timeout执行成功了,就进入catch
Promise.race([requestImg(), timeout()]).then((data) => {
  console.log(data);
}).catch((err) => {
  console.log(err);
})

手写Promise

好了这是最恶心的part,但也是面试官最享受的part

class Promise {
  constructor(executor) {
    // 默认当前为等待状态
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    
    // 存放成功的回调
    this.onResolveCallbacks = [];
    
    // 存放失败的回调
    this.onRejectedCallbacks = [];
    
    let resolve = (data) => {
      if (this.status === 'pending') {
        this.value = data;
        this.status = 'resolved';
        this.onResolveCallbacks.forEach(fn => fn());
      }
    }
    
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  
  then(onFulfilled, onRejected) {
    if (this.status === 'resolved') {
      onFulfilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
    if (this.status === 'pending') {
      // 存放成功的回调
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value);
      });
      // 存放失败的回调
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}