Promise,是一个代理对象,JavaScript 的异步操作解决方案,代表某个未来才会知道结果事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
1、Promise 含义
一种异步编程解决方案
Promise 是一个对象,获取异步操作的消息
使用new生成Promise对象的实例,构造函数内部的代码会立即执行。
1 | new Promise((resolve, reject) => { |
特点:
1、对象的状态不受外界影响。
pending(进行中),fulfilled(已成功),rejected(已失败)
只有异步操作的解构可以决定当前是那种状态,任何其他操作都没办法改变这个状态
2、一旦状态改变,就不会再变。
状态只能从 pending 变为 fulfilled,或是从 pending 变为 rejected
resolved 代表 fulfilled
缺点
1、中途无法取消
2、如果不设置回调函数,Promise 内部错误不会抛出到外部
3、当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
2、基本用法
Promise 实例
1 | const promise=new Promise((resolve ,reject)=>{ |
resolve 和 reject 函数是 js 引擎提供的。promise 实例生成以后,可以使用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
1 | promise.then( |
第二个回调函数是可选的,两个回调函数的参数都是生成实例的时候传出的
1 | const promise=new Promise((resolve ,reject)=>{ |
实例立刻执行所以先输出Promise,then里面的函数要等到所有同步任务执行完才执行,所以先输出hi再输出 resolved。
如果后面有多个then函数,会依次执行,第一个函数执行完后,第二个then的函数才会进入微任务队列。
图片加载异步操作
1 | function loadImageAsync(url){ |
reject 函数的参数通常是 Error 对象。
resolve 函数的参数有可能是数据,也有可能是另一个Promise实例
1 | const p1=new Promise((resolve ,reject)=>{ |
p2的状态一秒之后变为 resolve,返回的是p1,p1定义时 3 秒后状态变为reject,所以再过两秒,p1变为reject。由于p2返回的是另一个Promise,所以p2的状态失效,下面的两个回调根据p1的执行结果执行。因此执行.catch,打印 Error:fail。
注意,在创建Promise实例的时候调用函数resolve或是reject并不会终结 Promise的执行
1 | new Promise((resolve ,reject)=>{ |
Promise实例会立刻执行,执行完,才会执行.then里面的语句,所以先 2 后 1。
3、Promise.prototype.then
then 方法是定义在 Promise 原型对象上的 为 Promise 实例状态变化添加回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
1 | new Promise((resolve,reject)=>{ |
then 方法返回一个新的Promise实例,因此可以采用链式写法
1 | promise.then(result=>{ |
如果then方法的回调函数返回的是Promise实例,后面的then方法会根据这Promise实例的状态变化才执行。
1 | getJSON('/post/1.json').then( |
第一个then方法的回调函数返回一个新的Promise实例,第二个then会根据这个 Promise的执行结果来判断是执行resolved还是rejected。
4、Promise.prototype.catch
用于指定发生错误时的回调函数
当Promise实例执行的时候抛出的错误状态就会变为rejected,然后就会执行catch方法,并且错误会作为参数传入catch的回调函数。
另外 then 方法的回调运行时抛出错误也会被 catch 捕获
1 | p.then(val=>console.log('fulfilled',val)) |
reject方法,等同于抛出错误,throw Error
以下三种写法效果是一样的
1 | //1 |
如果状态已经变为resolved再抛出错误是无效的
1 | const Promise = new Promise(function(resolve, reject) { |
如果在Promise实例内部使用 try/catch,没有使用 catch 方法指定错误处理回调函数,抛出的错误不会返回到外部,即不会有任何反应
1 | const someAsync=function(){ |
Promise 内部的语法错误,但是 123 还是打印出来,不会退出进程,终止整个脚本的执行Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”
catch 方法返回的还是一个 Promise,因此后面可以继续使用 then方法
1 | Promise.resolve() |
上面的代码因为没有报错,跳过了catch方法,直接执行后面的then方法。此时,要是then方法里面报错,就与前面的catch无关了。catch 中也能抛出错误,并且可以被后面的 catch 捕获到。
5、Promise.prototype.finally
finally 方法用于指定不管 Promise 对象的状态如何都会执行的操作
1 | promise |
finally 方法本质是 then 方法的特例
1 | promise.finally(()=>{}) |
finally 实现
1 | Promise.prototype.finally=function(callback){ |
上面代码中,不管前面的 Promise 是 fulfilled 还是 rejected,都会执行回调函数 callback。
从上面的实现还可以看到,finally 方法总是会返回原来的值。
1 | // 直接是resolved状态,resolve 的值是 undefined |
6、Promise.all()
用于将多个Promise 实例包装成一个Promise 实例
1 | const p=new Promise([p1,p2,p3]) |
Promise.all()接收一个数组作为参数,参数都是 Promise 实例,如果不是 Promise 实例会调用 Promise.resolve 方法转为 Promise 实例
p 的状态由 p1,p2,p3 共同决定,分为两种情况
p1,p2,p3 的状态都是 fulfilled 的时候,p 的状态才会变成fulfilled,此时 p1,p2,p3 的返回值组成一个数组传递给 p 的回调函数
p1,p2,p3 之中有一个变为rejected状态,p 就是rejected状态,第一个变为 rejected的实例的返回值作为参数传递给 p 的回调函数
注意,如果作为参数的的Promise实例里面定义了 catch,一但它进入 rejected 状态,并不会触发Promise.all的catch方法
1 | const p1=new Promise((resolve ,reject)=>{ |
p2 没有设置自身的catch方法时,就会调用 promise.all 的catch方法
1 | const p1=new Promise((resolve ,reject)=>{ |
7、Promise.race()
同样是将多个Promise实例包裹后返回一个实例
1 | const p=new Promise([p1,p2,p3]) |
只要 p1,p2,p3 中的任何一个实例中的状态首先发生变化,p 的状态就跟着变化
率先发生变化的实例的返回值作为参数传入 p 的回调函数
同Promise.all一样如果传入的不是Promise的实例对象,就通过 Promise.resolve 转为Promise的实例
1 | const p=Promise.race([ |
5s 内 fetch 请求无法获取到返回结果,p 就会抛出超时错误
8、Promise.resolve()
将现有对象转为Promise对象
1 | const p=Promise.resolve($.ajax('/whatever.json')) |
将 Jquery 生成的 deferred 对象转为Promise对象
1 | Promise.resolve('foo') |
Promise.resolve 方法分为四种情况
1、参数是一个Promise实例
直接将实例返回,不做更改
2、参数是一个thenable对象thenable 对象是指具有then方法的对象
1 | let thenable={ |
Promise.resolve 会将这个对象转为Promise实例,然后立即执行thenable对象中的 then 方法
1 | const p=Promise.resolve(thenable) |
3、参数不是含then方法的对象,或者根本不是对象
如果参数是原始类型的值,或者是一个不具有then方法的对象。Promise.resolve 方法返回一个Promise对象状态是 resolved,Promise.resolve 方法的参数会传给后面then的回调函数
1 | const p=Promise.resolve('Hello') |
4、不带任何参数Promise.resolve直接返回一个 resolved 状态的Promise对象
该对象时在本次事件循环结束的时候执行setTimeout 在先出事件循环开始的时候执行
1 | setTimeout(()=>console.log('three'),0) |
9、Promise.reject()
返回一个状态为 rejected 的Promise实例对象,
1 | const p=Promise.reject('出错了') |
注意,Promise.reject 会将参数原封不动的传递给then方法的回调函数
1 | const thenable={ |
10、应用
Generator 函数与Promise结合
1 | function getFoo(){ |
上面代码的 Generator 函数 g 之中,有一个异步操作 getFoo ,它返回的就是一个Promise对象。函数 run 用来处理这个Promise对象,并调用下一个 next 方法。
手写 Promise
基础版本
1 | class MyPromise{ |
增强版代码
1 | class MyPromise { |
符合 PromiseA+规范的实现
1 | const STATUS = { |
