Promise

186 阅读5分钟

Promise

  • 主要用于异步计算
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。
  • 可以在对象之间传递和操作Promise,帮助我们更好地处理队列。

历史背景

  • JavaScript为检查表单而生。
  • 创造他的首要目标是操作DOM
  • 所以,JavaScript的操作大多是异步的,避免界面冻结,完成顺序和交付顺序无关。
1.异步操作的常见语法
  • 事件侦听与响应

  • 回调

    先调用一个外部函数,传递给他一个回调函数

2.浏览器中的JavaScript
  • 异步操作以事件为主

  • 回调主要出现在Ajax 和File API

    有了node.js(无阻塞,高并发,异步操作是其保障,大量操作依赖回调函数,稍有不慎,就会踏入回调地狱,不好维护)之后,对异步的依赖进一步加剧

    异步处理回调的时候,不能正常地使用try catch throw error的处理机制,因为不在一个栈中,需要return callback

    • 异步回调函数会在一个新的栈里执行,在这个栈里面,没办法获取之前栈的信息的,之前的栈,也没有办法捕获这个栈抛出的异常,所以不能使用try catch
    • 外层作用域声明变量,交给内层作用域来使用,存在被修改的可能性
3.回调有四个问题
  • 嵌套层次很深,难以维护
  • 无法正常使用return 和throw
  • 无法正常的检索堆栈信息(每一次回调,都是在系统层面上的新的堆栈)
  • 多个回调之间,难以建立联系(一个回调一旦开始启动,就没有办法对他进行操作)
4.promise
  1. promise
new Promise(
    //执行器 executor
	function(resolve,reject){
        //一段耗时很长的异步操作
        resolve();//数据处理完成
        reject();//数据处理出错
    }
)
	.then(function A (){
    	//成功,下一步
	},function B(){
    	//失败,做相应处理
	})
  • Promise是一个代理对象,他和原先要进行的操作并无关系
  • 它通过引入一个回调(把其他的回调展开,全部变成基于promise的回调),避免更多回调
  1. Promise有三个状态
    • pending[待定]初始状态
    • fulfilled[实现]操作成功
    • rejected[被否决]操作失败

当promise状态发生改变时,就会触发.then()里的响应函数处理后续步骤

promise的状态一经改变,不会再变

promise实例一经创建,执行器立即执行

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

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

​ 因为.then()返回的还是promise实例。会等里面的.then(执行完,再执行外面的。对于我们来说,此时最好将其展开,会更好读。)

![img](file:///C:/Users/lenovo2017lyp/AppData/Local/Packages/Microsoft.Office.OneNote_8wekyb3d8bbwe/TempState/msohtmlclip/clip_image001.png)

![img](file:///C:/Users/lenovo2017lyp/AppData/Local/Packages/Microsoft.Office.OneNote_8wekyb3d8bbwe/TempState/msohtmlclip/clip_image002.png)

![img](file:///C:/Users/lenovo2017lyp/AppData/Local/Packages/Microsoft.Office.OneNote_8wekyb3d8bbwe/TempState/msohtmlclip/clip_image003.png)

we-have-a-problem-with-promises

  1. promises 的奇妙在于给予我们以前的 returnthrow

    每一个 promise 都会提供给你一个 then() 函数 (或是 catch(),实际上只是 then(null, ...) 的语法糖)。

    当我们在 then() 函数内部时:

    我们可以做什么呢?有三种事情:

    1. return 另一个 promise (“composing promises”)

    2. return 一个同步的值 (或者 undefined)

    3. throw 一个同步异常

  2. 错误处理

    • reject ("错误信息").then(null,message=>{})

    • throw new Error('错误信息').catch(message=>{})

      推荐使用第二种,更加清晰好读,并且可以捕获前面的错误

      在catch里面处理错误时,仍然会返回promise实例,也有两个状态

      强烈建议在所有队列最后都加上.catch(),以避免漏掉错误处理造成意想不到的问题

6.Promise.all()
  • Promise.all([p1,p2,p3,...]) 用于将多个Promise实例,包装成一个新的Promise实例。

  • 返回的的实例就是普通的Promise

  • 接受一个数组作为参数

  • 数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变

  • 当所有子Promise都完成,该Promise完成,返回值是全部值的数组

  • 有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果

  • Promise.all()最常见的就是和.map()连用

  • 实现队列

    • 使用.forEach()

      function queue(things){
          let promise = Promise.resolve();
          things.forEach(thing =>{
              promise = promise.then( ()=>{
                  return new Promise( resolve => {
                      doThing(thing, ()=>{
                          resolve();
                      });
                  } );
              } );
          });
          return promise;
      }
      
      queue(['lots','of','things',...])
      
      • 常见错误:没有把.then()产生新Promise实例赋给promise,没有生成队列
    • 使用.reduce()

      function queue(things){
      	return things.reduce( (promise,thing) =>{
      		return promise.then( ()=>{
      			return new Promise( resolve =>{
      				doThing(thing,()=>{
      					resolve();
      				});
      			});
      		} );
      	},Promise.resolve() );
      }
      
      queue(['lots','of','things',...])
      
      • Promise实例创建之后,会立刻运行执行器代码,所以这个也无法达成队列的效果。
      7.Promise.resolve()

      返回一个fulfilled的Promise实例,或原始Promise实例。

      • 参数为空,返回一个状态为fulfilled的Promise实例

      • 参数是一个跟Promise无关的值,同上,不过fulfilled响应函数会得到这个参数

      • 参数为Promise实例,则返回该实例,不做任何修改

      • 参数为thenable,立即执行他的.then()

        对象里面有个then(){}

      8.Promise.reject()
      • 返回一个rejected的Promise()实例
      • Promise.reject()不认thenable
      9.Promise.race()

      类似Promise.all(),区别在于它有任意一个完成就算完成。

      • 把异步操作和定时器放在一起
      • 如果定时器先触发,就认为超时,告知用户
      10.promise常用做法
      • 把回调包装成Promise(可读性更好,返回的结果可以加入任何Promise队列)
      • 把任何异步操作包装成Promise