JavaScript--Promise

@wanqiuz 2018-07-09 15:18:47发表于 wanqiuz/blog-articles

为什么用Promise

JavaScript为检查表单诞生,创造它的首要目标是为了操作DOM,而GUI是单线程的,因此JavaScript设计成单线程。为了解决同步操作带来的界面冻结问题,JavaScript需要包含大量的异步操作。
异步,是将耗时久的工作A交给系统后,继续完成B工作,直到系统完成工作A后,通过回调或者事件,继续做A剩下的事情。
从观察者的角度看,AB工作的完成顺序,和开始的顺序无关,所以叫做“异步”。

怎么实现异步操作呢?
一种是事件侦听与响应(addEventListener等)
另一种是回调

回调会有什么问题?

  • 嵌套层次很深,难以维护
  • 无法正常使用return和throw(每次异步回调时都运行在全新的堆栈中)
  • 无法正常检索堆栈信息
  • 多个回调之间难以建立联系

如何构造

new Promise(
  // 同步执行
  function (resolve, reject) {
    //一段耗时很长的异步操作
    resolve(value1); //传入resolve的参数value1
    reject(value2); //传入reject的参数value2
  }
)
  .then(function A(value1) {
    //成功,下一步,异步
  }, function B(value2) {
    //失败,做相应处理
  });

Promise
在构造时(new Promise),构造器传入的参数是立即执行的,这个过程和.then()有没有定义没有任何关系,在执行过程中Promise是pending状态。
Promise有三个状态pending、fulfilled、rejected
当Promise状态发生改变时,就会触发.then里面的响应函数。
状态一经改变,不会再改变。

.then()

  • .then()接受两个函数作为参数,分别代表fulfiilled和rejected
  • .then()返回一个新的Promise实例,所以它可以链式调用
  • 当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行
  • 状态响应函数可以返回新的Promise,或其他值,或没有值
  • 如果返回新的Promise,那么下一级.then()会在新Promise状态改变后执行
  • 如果返回其他任何值或者不返回,则会立刻执行下一级.then()

.then()里有.then()的情况

  • 因为.then()返回的是还是Promise实例。
  • 会等里面的.then()执行完,再执行外面的
  • 对于这种情况,最好将其展开

四个问题:

假设doSomethingElse返回一个Promise
qq20180709-081421
问题1:
qq20180709-081452
问题2:
qq20180709-081506

问题3:
qq20180709-081528

问题4:
qq20180709-081630

错误处理

  • reject("错误信息“) .then(null, message => {})
  • throw new Error("错误信息”) .catch(message => {})
  • 推荐使用第二种,更加清晰好读,并且可以捕获前面的错误

Promis.all()

  • 它接受一个数组作为参数
  • 数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
  • 当所有子Promise都完成,该Promise完成,返回值是全部值的数组
  • 有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果

promise.all()的使用:
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

Promise.all方法接受一个数组作为参数,数组里的元素都是Promise对象的实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例。)

var p =Promise.all([p1,p2,p3]);
1
p的状态由p1、p2、p3决定,分为两种情况。

当该数组里的所有Promise实例都进入Fulfilled状态,Promise.all返回的实例才会变成Fulfilled状态。并将Promise实例数组的所有返回值组成一个数组,传递给Promise.all返回实例的回调函数
当该数组里的某个Promise实例都进入Rejected状态,Promise.all返回的实例会立即变成Rejected状态。并将第一个rejected的实例返回值传递给Promise.all返回实例的回调函数。

封装一个函数,参数是定时器的时间,.then执行回调函数。

function sleep (time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}

Promise方法你最常用什么写法?构造函数传入的参数是什么类型?那么,传入的该函数是会立刻执行的吗?若调用了两次resolve方法会怎么样?发生异常会怎么样?

Promise是一个构造函数,自己身上有all、race、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。这么说用Promise new出来的对象肯定就有then、catch方法喽。
当通过Promise构造函数实例化一个对象时,会传递一个匿名函数作为参数,而且这个函数在新建一个Promise后,会立即执行。所以传入的该函数是会立刻执行的。

会在本次new 操作立刻执行。。
第一次resove就确定了自己是成功还是失败。第二次没用了。添加reject也改变不了。
Promise 一旦决议了,之后的决议就没有用了,状态无法改变的。会忽略

补充resolve方法的两个用途:(1)改变状态;(2)传参
我们会在异步操作成功时调用resolve函数,其作用是将Promise对象的状态从Pending变为Resolved,并将异步操作的结果,作为参数传递给Fulfilled状态的回调函数。

Promise的状态透传:
then方法必须返回一个promise实例
promise2 = promise1.then(onFulfilled, onRejected);
  2.2.7.3. 如果 promise1的 onFulfilled 不是函数,那么promise1的不可变值将传递到promise2并作为promise2的不可变值。
  2.2.7.4. 如果 promise1的 onRejected不是函数,那么promise1的不可变原因将传递到promise2并作为promise2的不可变原因,并作为promise2的 onRejected 的入参。

请大家记住三点:
回调方法中一定要使用return语句,避免调用者丢失其处理状态与结果。
在promise实例的最后使用catch方法,用来做整体的异常捕获与处理。
利用Promise.then方法对回调函数返回结果的封装,写出清晰漂亮的链式调用代码。

.catch
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

qq 20180711093141

参考并感谢: