聊聊JavaScript中浪漫的Promise(一)

I promise

​简单介绍一下JS的异步编程

我们知道JavaScript的执行环境是单线程(single thread)的,也就是所有的代码都是从上至下按照顺序依次执行的,这种模式的优点是非常的简单,缺点也很明显:如果前一个任务需要花费很长的时间,那么后面的任务只能等着。因此,为了解决这个问题,JavaScript把任务的执行模式分为“同步(Synchronous)”和“异步(Asynchronous)”两种。 同步任务,就是我们熟悉的从上至下按照顺序执行的任务执行方式,而所谓异步,就是每一个任务都有一个或多个回调函数(callback),这个任务并不会影响其他任务的执行,只是在一段时间(取决于该任务的执行时间)之后,再调用之前等待的回调函数,这样就形成了任务的执行顺序和排列顺序是不一致了,也就是异步的。

这种典型的运用于单线程的编程方式就被称为异步非阻塞I/O模型(Asynchronous Non-blocking I/O Model)。而最典型的异步例子就是AJAX请求,想象一下,假如有10个请求,他们之间互不影响,是一个请求回来再发送另一个请求合适呢,还是说一次性全部发送请求,然后等待这些请求一个一个的返回?显然后者是更合适的模式,而且这种模式对于单线程的客户端执行环境是必须的,甚至是唯一的解决方案。 ES6之前,我们处理异步编程一般都是通过回调函数callback,如下:

然而写着写着就这样了:

这种代码风格真的很折磨人!(看得我都脱发了!) 因此,JS社区有越来越多的针对异步编程的解决方案,包括事件监听、发布订阅、生成器Generators/yield、Promise以及最新的Async/Await。 而其中比较有意思又比较重要的就是我们今天要讨论的Promise啦!(撒花...终于到正题啦!)

什么是Promise?

本质上,一个 Promise 就是一个对象,它代表了一个异步操作的最终完成或者失败,而且一定会实现,即一定会是成功或者失败,这也就是Promise的字面意思所在,它承诺一定会给出一个最终的状态,听起来是不是有点浪漫呢,简单明了,再也不用扭扭捏捏得不到回应啦!(嗯?我在说什么...)

Promise的特点

Promise一共有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。一个Promise的状态只取决异步操作的结果,而不受任何其他影响。Promise的状态改变只有2种形态,从pending变为fulfilled和从pending变为rejected,一旦改变就不可再次改变。

Promise的基本用法

首先我们new一个Promise的实例,Promise对象接收一个函数参数,该函数接收2个固定参数resolve和reject,这2个参数本质都是回调函数,resolve(reject)的作用是分别将Promise对象的状态从 pending 变为 resolved(rejected),然后在异步操作成功(失败)时调用,将异步操作的结果包装成一个新的Promise对象传递出去,之后我们就可以通过.then()和.catch()分别接收到传递出来的结果,进行下一步的操作;

Promise的链式调用

Promise.then()和Promise.catch()能接收resolve和reject传递的结果,是因为这些结果都是一个新的Promise对象,如果.then()和.catch()中还是有返回(即有return),那么我们能继续用.then()或者.catch()调用,这样的调用模式就称为Promise的链式调用,如下图:

Promise Chain

Promise的运用

Promise最大的用处就是对于AJAX请求的优雅处理:

ajax with Promise

Promise的缺点

  1. Promise是无法取消的,一旦进行就无法中断;
  2. Promise对于错误的处理还不够完善,Promise的错误需要通过回调函数捕获,而且在多个.then()的链式调用时,无法确定错误是哪一个调用发生的;

结语

虽然Promise还有不足,但是相对于回调地狱,已经属于非常不错的解决方案了,那么,对于Promise的这些不足,我们是否有方法处理呢? 答案是肯定,但是...我们下次再聊啦