Promise对象

333 阅读4分钟

概述

Promise是javascript异步操作统一解决方案,它在异步操作和回调函数中起到中介作用,promise能够让异步操作写起来不必一层一层嵌套回调函数,增加代码的可读性。

传统的回调函数

由于回调函数是作为参数传入另外一个函数中,而异步操作的结果一般为这个回调函数的参数,假设我需要在f1执行后调用f2,传统的写法会是这样的

function f1(fn){
	fn()
}
function f2(x){
	console.log(x)
}
f1(f2('hello')) //hello

但是一旦有些任务需要多层嵌套,代码就会写成

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // ...
      });
    });
  });
});

而使用promise就极为清晰,它使用返回实例对象的then方法,增加代码的可读性

let promise =new Promise(step1)
promise.then(step2).then(step3)

Promise构造函数

JS提供原生写法,来生成Promise实例

let promise=new Promise((resolve,reject)=>{
	if('操作成功'){
    resolve()
    }else{
    reject()}
})

Promise构造函数接收一个函数为参数,函数中又接收两个函数作为参数,当异步操作成功时,执行resolve函数,不成功则执行reject。

实例状态

Promise对象根据自身的状态来控制异步操作,Promise实例有三种状态:

1、异步操作未完成(pending)

2、异步操作成功(fulfilled)

3、异步操作失败(rejected)

这三种状态只有两种状态转化结果:

  • 从未完成到成功
  • 从未完成到失败

一旦状态发生变化,就不再改变,这也是Promise的由来,意味着“承诺”

因此,Promise的结果有两种,成功则返回一个值,并将状态改成fulfilled

失败则抛出一个error,状态改成rejected

resolve和reject函数

resolve函数的作用是,将Promise实例的状态从“未完成”变为“成功”(即从pending变为fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

reject函数的作用是,将Promise实例的状态从“未完成”变为“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

let a=new Promise((resolve,reject)=>{
	setTimeout(reject,1000)
})

上面的代码是1秒钟过后,执行reject函数,此时实例对象a的状态会发生变化

// a
[[PromiseState]]: "rejected"
[[PromiseResult]]: undefined

使用promise封装ajax

function ajax(method, url, data) {
    const request = new XMLHttpRequest()
    return new promise((resolve, reject) => {
        request.open(method, url)
        request.onreadystatechange = () => {
            if (request.readyState === 4 && request.status === 200) {
                const obj = JSON.parse(request.response)
                resolve(obj)
            } else {
                reject(request.status)
            }
        }
        request.send(data)
    })
}

Promise.prototype.then()

Promise 实例的then方法,用来添加回调函数。

then方法可以接受两个回调函数,第一个是异步操作成功时(变为fulfilled状态)的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。

var p=new Promise((resolve,reject)=>{
	resolve('我成功了')
})
p.then((data)=>{console.log(data)},(data)=>{console.log('失败'+data)})
//我成功了

上面的代码返回一个直接执行Promise后面的函数,并且在函数中继续运行resolve函数,运行后返回一个promise对象给p,在p的基础上再继续调用回调函数,由于此时p的状态被resolve函数改变成fulfilled,所以会执行then后的第一个函数。

then的链式操作

每次then之后都会返回一个新的Promise对象,所以then是可以链式操作的

p1
  .then(step1)
  .then(step2)
  .then(step3)
  .then(
    console.log,
    console.error
  );

上面代码中,p1后面有四个then,意味依次有四个回调函数。只要前一步的状态变为fulfilled,就会依次执行紧跟在后面的回调函数。

最后一个then方法,回调函数是console.log和console.error,用法上有一点重要的区别。console.log只显示step3的返回值,而console.error可以显示p1、step1、step2、step3之中任意一个发生的错误。举例来说,如果step1的状态变为rejected,那么step2和step3都不会执行了(因为它们是resolved的回调函数)。Promise 开始寻找,接下来第一个为rejected的回调函数,在上面代码中是console.error。这就是说,Promise 对象的报错具有传递性。