promise

Promise

1、promise是什么

传统异步编程的方式是发送请求,成功之后回调函数,即回调函数和事件的解决方案。这种方式解决异步编程容易出现回调地狱,即当回调函数中还存在回调函数多层嵌套的话,代码不仅不方便阅读,还容易错乱,如:

setTimeout(function(){
    setTimeout(function(){
		setTimeoue(function(){
            console.log('1')
        },1000)
    },1000)
},1000)

通过promise,我们可以更好的解决他。

因此,promise是异步编程的一种解决方案

promise最早由社区提出和实现,ES6 将其写进了语言标准,统一了用 法,原生提供了 Promise 对象。

因此,promise是一个对象,从它可以获取异 步操作的消息。

Promise 提供统一的 API,各种异步操作都可以用同样的方法进行 处理。

简单来说,promise是一个容器,里面保存着未来执行失败或执行成功的结果

2、promise的特点

  1. 对象的状态不受外界影响。

    promise对象代表一个异步操作,这个操作有三种状态,pending(进行中),fulfilled(已成功),rejected(已失败)。只有异步操作的结果可以决定当前是哪种状态,其他任何操作都无法改变这种状态,这也是promise(承诺)的由来

  2. 状态一旦改变,就不会再变,任何时候都可以得到这个结果。

    状态只能从pending变成fulfilled,或者从pending变成rejected,这个时候称为resolved(已定型)。如果改变已经发生,再用回调函数去得到这个结果,依然还是这个结果,这与事件不同,事件监听错过了,就得不到结果了。

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层 嵌套的回调函数。此外, Promise 对象提供统一的接口,使得控制异步操作更加 容易。

3、promise的缺点

  • 无法取消promise,一旦新建promise对象,他就会立即执行,无法中途取消
  • 如果不设置回调函数,内部抛出的错误不会反应到外部
  • 处于pending状态时,无法得知目前进展到哪一个阶段

stream模式?

4、基本用法

ES6 规定, Promise 对象是一个构造函数,用来生成 Promise 实例。

let promise = new Promise((resolve, reject) => {
  // some code
  if (true) {
    // 请求成功 将结果传入,然后传递出去
    resolve(value)

  } else {
    // 请求失败 将错误结果传入,然后传递出去
    reject(error)
  }

})


promise.then(value => {
  // 可以拿到上面传入的结果
  console.log(value);
}, error => {
  // 可以拿到上面传入的错误信息
  console.log(error);
})

其中promise构造函数需要传入两个参数,两个参数又是两个函数

resolve函数在状态变为resolved的时候执行,rejected函数在状态变为rejected的时候执行

这两个函数传入的参数分别对应成功结果和错误结果,又可以在后面拿到promise实例之后,调用then方法绑定的回调函数中拿到

then方法传入两个函数,分别对应成功的回调和失败的回调,参数即可拿到上面传入的参数

与传统异步编程解决方案不同的是,只要resolve或reject其中一个执行,无论在什么时候调用then方法都可以拿到结果,而不是等事件过了之后,就无法拿到结果了

promise封装ajax请求

function getData(url) {
      return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.setRequestHeader('Accept', 'application/json')
        xhr.responseType = 'json'
        xhr.send()

        xhr.onreadystatechange = function() {
          if (this.readyState == 4) {
            if (this.status == 200) {
              resolve(this.response)
            } else {
              reject(new Error('出错了:' + this.statusText))
            }
          }
        }
      })
    }

    getData(url).then(res => {
      console.log(res);
    })

5、关于一些执行顺序

promise新建之后就会立即执行

let promise = new Promise((resolve, reject) => {
  console.log('create promise');
  resolve('ready')
})

promise.then(res => {
  console.log(res);
})

console.log('hi promise');
// create promise
// hi promise
// ready

其中,create promise会在创建时立马执行,接着执行同步任务hi promise,最后是异步任务ready

6、promise的原型方法

6.1 Promise.prototype.then()

前面已经知道then()方法的使用,但要注意的是then()方法返回的还是一个对象,但是不是上一个promise对象,而是一个新的promise对象,因此可以链式调用

getData(url).then(res => {
      return res.status
    }).then(res => {
      console.log(res); //res.status
    })

相同的,如果上一个then()返回的是一个promise对象(自己返回),那么下一个then()中还是可以传入两个参数,一个resolve一个reject,它会等待上一个promise的状态改变去执行对应函数

getData(url).then(res => {
      return getDate(res.url)
    }).then(res => {
      console.log(res); //会等待getData返回的promise的状态改变
})

6.2 Promise.prototype.catch()

该方法是then()方法中第二个参数的别名,即如果发生错误,会被这个方法捕获

then方法中回调函数的错误也会被这个方法捕获

// 写法1
let promise = new Promise((resolve, reject) => {
  throw new Error('test')
}).then(null, err => console.log(err))

// 写法2
let promise = new Promise((resolve, reject) => {
  throw new Error('test')
}).catch(err => console.log(err))

// 写法3
let promise = new Promise((resolve, reject) => {
  try {
    throw new Error('test')
  } catch (e) {
    reject(e)
  }

}).catch(err => console.log(err))

但是,前面说到,promise的状态一旦改变,是不会再变化的,所以如果在resolve之后抛出异常,是无效的

promise对象的错误具有“冒泡”性质,前面的错误总可以传递到后面,被最后一个catch捕获到

同时,建议使用catch方法来捕获错误而不使用then方法的第二个参数

前面也说到,promise的缺点是错误不会被外部得知,如果不设置catch,因此建议总是带上catch方法

前面说到then()方法返回的是一个promsie对象,catch()方法也是一样,因此,catch()方法也可以链式调用,后面可以继续跟上then()

前面说到,promise的错误不会传递到外层,因此如果catch方法中报错了,外面不会知道,因此应该在有内部代码的catch后面增加一个catch用于捕获上一个catch中逻辑代码的错误。

6.3 Promise.all()

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

var p = Promise.all([p1, p2, p3]);

传入参数是一个数组,数组中的每个元素必须是promise对象,如果不是则会调用promise.resolve方法将他转成promise实例==( Promise.all 方法的参数可以不是数 组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)==

p的状态由传入的数组中的每个元素决定:

  • 只有数组中的全部promise状态都是fulfilled,p才是fulfilled
  • 只要有一个promise状态是reject则p为reject

要注意的是如果数组中的promise实例如果rejected被捕获了之后,p是捕获不到该错误的,因为promise实例catch之后返回了一个新的promise,该promise传入的参数是那个错误的信息,虽然输出了,但实际上不是catch捕获的,而是resolve输出的

6.4 Promise.race()

Promise.race 方法也用于将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.race([p1, p2, p3]);

传入参数是一个数组,数组中的每个元素必须是promise对象,如果不是则会调用promise.resolve方法将他转成promise实例==( Promise.all 方法的参数可以不是数 组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)==

p的状态由传入的数组中的每个元素决定:

  • 只要有一个元素率先改变状态,就返回那个状态

这个方法可以应用于限制一个函数的响应时间,传入两个promise实例,一个实例用于请求,一个实例用于抛出异常

Promise.race([getData(url),
    new Promise((resolve, reject) => setTimeout(reject(new Error('')), 5000))
  ])
  .then(res => console.log(res)
    .catch(err => console.log(err)))

代码表示,如果第一个promise对象5s内未响应的话,第二个promise就会执行

6.5 Promise.resolve()

Promise.resolve用于将现有的对象转换为promise对象

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

resolve参数有4种情况:

  1. 参数是promise实例

    不做任何操作,直接返回这个实例、

  2. 参数是带then方法的对象

    Promise.resolve({
        then((resolve,reject)=>{
        resolve(1)
    })
    }).then(res=>console.log(res)) //1
    
  3. 参数不是一个对象

    resolve将会把他转换为一个对象然后返回

  4. 不带参数

    不带参数则会直接返回一个resolved状态的promise对象

    因此创建promise对简单的方式就是

    let p = Promise.resolve()
    

需要注意的是,立即 resolve 的Promise对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three

6.6 Promise.reject()

与resolve方法类似,该方法也返回一个promise对象,但是状态是rejected

注意:该方法的参数会原封不动的作为后面catch方法的参数

const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// catch方法的参数就是reject传入的参数

6.7 两个有用的附加方法

done()

promise链式回调,但是最后一个then或者catch总是不能被捕获到错误的,因此我们可以写一个done方法,将他至于末端用于捕获错误

Promise.prototype.done = function (onFulfilled, onRejected) {
    this.then(onFulfilled, onRejected)
        .catch(function (reason) {
        // 抛出一个全局错误
        setTimeout(() => { throw reason }, 0); //写setTimeout的原因是:如果是这样写函数,则promise函数体已经执行结束,抛出错误的这个函数实际上是全局的函数,这样错误就可以向全局抛出
    });
};

finally()

该方法用于指定,无论promise执行结果如何,都必须执行的操作,方法传入一个回调函数作为参数,该方法必须执行

应用场景如:监听服务器,最后必须关闭服务器

Promise.prototype.finally = function (callback) {
    return this.then(
        value => Promise.resolve(callback()).then(() => value),
        reason => Promise.resolve(callback()).then(() => { throw reason })
    );
};

6.8 Promise.try()

实际开发中,经常遇到一种情况:不知道或者不想区分,函数 f 是同步函数还是 异步操作,但是想用 Promise 来处理它。因为这样就可以不管 f 是否包含异步操 作,都用 then 方法指定下一步流程,用 catch 方法处理 f 抛出的错误。一般 就会采用下面的写法。

Promise.resolve.then(f)

但是这样写会有一个缺点,f如果是同步的,他会变成异步的,然后在本轮事件循环末尾执行

改善:两种写法

  1. 使用async

    const f = () => console.log('now')
    (async () => f())().then().catch()
    console.log('next')
    
  2. 使用promise

    const f = () => console.log('now');
    (() => new Promise(resolve =>
      resolve(f())
    ))().then().catch()
    console.log('next');
    

因为这是很常见的业务,因此现有提案将Promise.try代替上面的写法

因此以后可以这么写

Promise.try(...).then(...).catch(...)

用于捕获所有的错误

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值