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 = { |