ES6中的Promise

378 阅读5分钟

Promise出现之前

在promise出现之前,我们处理异步请求是

$.ajax({
        type:'post',
        url:'/api/xxx',
        success:(res)=>{
        	
        }
    })

这样看起来好像并没有什么问题,那么当我们下一个异步请求需要依赖于上一个异步请求的结果的时候,我们就需要这样

$.ajax({
        type:'post',
        url:'/api/xxx',
        success:(res)=>{
        	if (res.success) {
        	    //其他操作
	            $.ajax({
	            	type: 'get',
	            	url: '/api/yyy',
	            	success: (data) =>{
	            		 //其他操作
	            	}
	            });
        	}
        }
    })

如果在这个异步请求中我们还需要嵌套异步请求,这样一步步下去,就形成了我们常说的回调地狱,这在之后的代码维护中是非常困难的。

回调地狱的缺点:

  • 代码臃肿
  • 耦合性高,维护性差
  • 可读性差
  • 代码复用差
  • 容易存在一些bug
  • 只能在回调中处理异常

我还记得一句话:程序员是勤奋的,也是懒惰的,我觉得这句话很精辟,不然怎么会出现那么多好用又便捷的技术呢!

所以能发现美的眼睛的程序员就放弃那丑陋的回调地狱,找到了Promise,这位妥妥的大帅哥,并且一诺千金,从不毁约,他承诺未来会给你一个答复,那么在未来,无论是答应还是不答应,他都会回复你,不会吊着你的,哈哈!

什么是Promise

在ES6中,新增了一个API,它就是Promise,其实上面我已经说过了,promise就是为了解决回调地狱这个大麻烦,它其实就是一个容器,容器的本身不是异步的,但在容器里面往往封装了一个异步任务

与回调地狱代码相比

function ajax(url, type) {
    return new Promise((resolve, reject) => {
        $.ajax({
            type: type,
            url: url,
            success:(res)=>{
            	if (res.success){
                    resolve(res.data);
                } else {
                    reject(error);
                }
            }
        })
    })
}
ajax('/api/xxx','post')
//then方法第一个参数接收的函数就是容器中的resolve
//then方法第二个参数接收的函数就是容器中的reject
.then((value) => {
    console.log(value)
    //return一个promise对象,下一个.then的第一个参数接收的函数就是这个promise中的resolve
    return ajax(...)
})
.then(...)
...

从上面代码我们可以看出来,君子坦荡荡呀!Promise这位大帅哥,将自己的心意一个接着一个表示出来(链式调用),没有半分隐藏,自己不高兴,高兴都是能看出来的(也就是能很容易的捕获异常信息),再看看那个小人回调地狱,需要像剥洋葱一样,一层一层拨开,才能看到他的心思。我想,看完这个,大家心中已经有了自己选择的如意郎君了吧!

特点

Promise就是一个容器,里面存放了将来才可能完成的事情(异步),默认在这个异步操作中分为三种状态:

  • pendding
  • fulfilled
  • rejected

如果一个promise对象处在fulfilled或rejected状态而不是pending状态,那么它也可以被称为settled状态, 解释来自 MDN

Promsie的状态是不受外界影响的,只有异步操作的结果可以决定当前是什么状态,任何其他的操作都不会影响这个状态。

这也是那个承诺,当别人给你了一个承诺,只能听到他亲口回答才能够回应,当他没有回答的时候,无论怎么,都不相信也不给回应,不能被小人欺骗了呢!(就是当没有给状态的时候,即使使用了then方法,也不能执行里面的程序

pendding状态,就是正在执行,比如读取文件,读取成功之后,状态就会发生变化,要么成功,要么失败。就像前面说的承诺给你一个答复,无非两种,要么就是答应,要么就是不答应。

成功就是fulfilled,失败就是rejectedpendding的状态一旦发生变化,就不能再改变,这就相当于状态定型了,也就是resolved(定型,之前的resolve只指fulfilled)。总不能答应了人家又反悔吧!之前我们可说过,Promise是一个坦坦荡荡的君子呢!听话,咱不能做这种事情~

API

Promise.resolve(value)

Promise.resolve()是返回一个定值解析后的Promise对象。

value参数有以下几种情况:

  • 参数是一个实例,那么Promise.resolve(value)不会进行任何的修改,直接返回这个实例。
  • 参数是一个thenable对象(即带有then方法),如下代码,转换为Promise对象后会执行thenable中的then方法,then方法中执行了resolve()这个参数,那么p1.then中的也就是resolved状态。其实我的理解就是p1.then中接收的函数就是then函数中的resolve参数
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
  • 参数不是对象,也不具有then方法,那么该方法就会以成功状态返回一个新的Promise对象。
  • 不带任何参数,可以直接调用Promise.resolve()方法得到Promise对象

promise中提到的thenable其实就是一个非常类似 Promise 的东西。最简单的例子就是 jQuery.ajax,它的返回值就是 thenable 对象。但是要谨记,并不是只要实现了 then 方法就一定能作为 Promise 对象来使用。借鉴于@lucefer

Promise.reject(reason)

Promise.reject()返回一个带有拒绝原因的Promise对象。

Promise.resolve()不同的是返回的状态是rejected

Promise.race(iterable)

Promise.race()返回一个promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

它是将多个Promise实例,包装成一个新的Promise实例。就是当多个Promise任务同时执行的时候,返回最先改变状态的Promise实例的结果。

Promise.all(iterable)

Promise.allPromise.race()相似,接收一个数组作为参数,如果参数中的值不是Promise实例,就会调用Promise.resolve()

当iterable中的参数状态都变成fulfilled或者参数中不包含promise的时候,才会回调完成resolve,也就是Promise.all(iterable)的最终状态才会是fulfilled

如果其中一个状态为rejectedPromise.all(iterable)的状态就会是rejected,那么该实例就会回调失败reject

以上API只是一部分,更多的API大家可以查看阮一峰 Promise

总结

MDN中,解释Promise对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。
它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。
这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象

再说说Promise的使用过程

  • 初始化Promise对象

    • new Promise(fun)
    • Promise.resolve(fun)
  • 调用上一步返回的Promise对象中的then方法

    • then方法第一个参数接收的函数就是容器中的resolve
    • then方法第二个参数接收的函数就是容器中的reject
  • 注册catch异常处理函数,对回调抛出的异常进行处理

最后借用MDN官网的图,更直观的看看Promise的一些操作吧。

参考文章:
面试精选之Promise
ECMAScript 6 入门 - 阮一峰

注:此文为本人学习过程中的笔记记录,如果有错误或者不准确的地方请大佬多多指教~