期约 promise——异步编程新方案

目录

回顾:

同步回调和异步回调

错误处理

错误类型

错误处理

错误对象

promise

基本编码流程

状态

三个状态:

状态的改变:只有两种

promise构造函数

then (Promise.prototype.then)方法

then 方法的特点:

Promise.prototype.catch()方法

Promise.prototype.finally() 方法

Promise.all() 方法

Promise.race()方法

Promise.allSettled()方法

Promise.any() 方法

Promise.resolve() 方法

Promise.reject() 方法 

改变promise实例状态

改变promise实例的状态和指定回调函数谁先谁后呢?

then的链式调用

中断链式调用

 promise错误穿透

promise优势

小结


回顾:

同步回调和异步回调

同步的回调函数是立即在主线程上执行不会放入回调队列中;异步的回调不会立即执行,会放入回调队列中以后执行。

错误处理

错误类型

  • Error:所有错误的父类型
  • ReferenceError:引用的变量不存在
  • TypeError:数据类型错误
  • SyntaxError:语法错误

错误处理

捕获错误:可以用try{ ..}.catch(){...}

try里面放可能出错的代码,一旦出错立即停止try中的代码执行,并调用catch携带错误信息

抛出错误:throw error 

throw new Error("错误")

错误对象

message属性:错误的相关信息

stack属性:记录信息(错误的代码的位置等等)

promise

  1. 从语法上说promise是一个构造函数(内置);从功能上说promise的实例对象可以用来封装一个异步操作,并可以获取成功/失败的值。
  2. new promise 时,要传入一个函数(回调函数),它是同步的回调,会立即在主线程是执行,它被称为executor函数(执行器函数)
  3. 每一个promise 实例都有三种状态,分别是:初始化(pending)、成功(fulfilled)、失败(rejected)
  4. 每一个promise实例在刚被new出来的时候,状态都是 初始化(pending)。
  5. executor函数会接收两个参数。它们都是函数,分别用形参 resolve、reject 接收。
    • 调用 resolve 会让promise实例的状态变为 成功(fulfilled) ,同时可以指定成功的value(值)
    • 调用 reject 会让promise实例的状态变为 失败(rejected) ,同时可以指定失败的reason(原因)
const p = new Promise((resolve),(reject) => {
    resolve('ok'),
    reject('no')
})
// p接收实例化的Promise 
// news实例化Promise 
// resolve 成功
// reject 失败

基本编码流程

  1. 创建Promise的实例对象(初始化(pending)状态),传入 executor 函数
  2. 在 executor 中启动异步任务(定时器、ajax请求)
  3. 根据异步任务的结果,做不同的处理:
    1. 异步任务成功:调用resolve(value),让Promise实例对象状态变为成功(fulfilled),同时指定成功的value值。
    2. 异步任务失败:调用reject(reason),让Promise实例对象状态变为失败(rejected),同时指定失败的reason原因。
  4. 通过then方法为Promise的实例指定成功、失败的回调函数,来获取成功的value、失败的reason(注意:then方法所指的的成功的回调和失败的回调都是异步的回调。

状态

三个状态:

  • 初始化(pending):未确定的初始状态
  • 成功(fulfilled) :调用esolve(value) 之后状态
  • 失败(rejected) : 调用reject(reason)之后状态

状态的改变:只有两种

pending——fulfilled

pending——rejected

要注意的是状态只能改变一次。

一个promise指定多个成功/失败的回调函数,都会放入队列依次执行。(依次调用成功或者失败的回调函数)

promise构造函数

new promise (executor){ }

executor函数是同步执行的,(resolve),(reject) => { }

  • 调用 resolve 会让promise实例的状态变为 成功(fulfilled) ,同时可以指定成功的value(值)
  • 调用 reject 会让promise实例的状态变为 失败(rejected) ,同时可以指定失败的reason(原因)

executor 函数会在 promise 内部立即调用,异步代码放在 executor 函数中。

then (Promise.prototype.then)方法

Promise.prototype.then方法,Promise实例.then(onFulfilled,onRejected);

onFulfilled :成功的回调函数(value)=>{}

onRejected:失败的回调函数(reason)=>{}

then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。 then方法会返回一个新的promise实例对象。

then 方法的特点:

在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用,但通过 .then 形式添加的回调函数,不论什么时候,都会被调用。可以多次调用.then,(可以添加多个回调函数)它们会按照插入顺序并且独立运行。

then的链式调用我会在下面详细解释。

Promise.prototype.catch()方法

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。(它是then方法的语法糖)

异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误,另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

// 例如 
const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test

reject()方法的作用,等同于抛出错误。

/ 写法一
const promise = new Promise(function(resolve, reject) {
  try {
// throw 抛出错误
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
// reject() 抛出错误
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});

如果Promise 状态已经变成resolved,再抛出错误是无效的。

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。如果没有报错,则会跳过catch()方法。

由于catch()方法之中,还能再抛出错误。 所以还能调用第二个catch()方法用来捕获前一个catch()方法抛出的错误。

Promise.prototype.finally() 方法

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
// 不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

// 例如:服务器使用 Promise 处理请求,然后使用finally方法关掉服务器。
server.listen(port)
  .then(function () {
    // ...
  })
  .finally(server.stop);

finally方法里面的操作,是与状态无关的,不依赖于 Promise 的执行结果。 

finally方法总是会返回原来的值。

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

Promise.all() 方法

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

Promise.all(PromiseArr) 

PromiseArr包含n个Promise实例的数组,会返回一个新的Promise实例,只有所以的Promise成功才成功,只要有一个失败就返回失败。

Promise.race()方法

Promise.race(PromiseArr) 

PromiseArr包含n个Promise实例的数组,会返回一个新的Promise实例,但是成功还是失败取决于最先出结果的promise。

Promise.allSettled()方法

Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。

只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

Promise.any() 方法

Promise.any() 方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

Promise.resolve() 方法

Promise.resolve(value) 用于快速返回一个状态为fulfilled或者rejected的promise实例对象。

value的值可能是:

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

(2)参数是一个thenable对象

thenable对象指的是具有then方法的对象。Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。

(3)参数不是具有then()方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved

(4)不带有任何参数

Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。

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

比如: 有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就可以做到。

Promise.reject() 方法 

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

可以用于快速返回一个状态为rejected的Promise实例对象。

const p0 = Promise.reject(100)
const p = Promise.resolve(p0)

p.then({
    value=>{console.log('成功了',value);}
    reason=>{console.log('失败了',reason);
})
// 状态为失败,因为p的值是失败的promise。

改变promise实例状态

  1. 执行 resolve(value):如果当前是pending就会变成fulfilled

  2. 执行 reject(reason):如果当前是pending就会变成rejected

  3. 执行器函数(executor)抛出异常:如果当前是pending就会变成rejected

改变promise实例的状态和指定回调函数谁先谁后呢?

  1. 都有可能,正常情况下先指定回调在改变状态,但也可以先改状态再指定回调。
  2. 先改状态再指定回调可以先延迟一会儿再调用then
  3. promise实例什么时候才得到数据呢? 如果先指定的回调,那么当状态发生改变时,回调函数就会调用得到数据;如果先改变的状态,那么当指定回调时,回调函数就会调用得到数据。

then的链式调用

promise实例的then()返回的是一个新的promise实例,它的值和状态由:

  1. 由then() 所指定的回调函数执行的结果决定。
  2. 如果then所指定的回调函数返回一个非promise的值 a  ,则新的promise实例状态为成功(fulfilled),value为 a 。
  3. 如果then所指定的回调函数返回一个promise的值 p  ,则新的promise实例状态和值与 p 一致
  4. 如果then所指定的回调函数抛出异常,如(throw),则新的promise实例状态为rejected,reason为抛出的异常。

promise串联多个异步任务也是通过then的链式调用。

中断链式调用

当使用promise的then 链式调用时,在中间中断,不再调用后面的回调函数,可以在失败的回调函数中返回一个padding状态的promise实例。

// reason 中返回状态为padding的Promise。 这样失败后就不会继续调用下去了。
return new Promise(()=>{})

 promise错误穿透

当使用promise的then链式调用的时候,可以在最后用catch指定一个失败的回调。当前面任何操作出现错误都会传到最后失败的回调中处理了。

如果不存在then的链式调用那么就不需要使用then的错误穿透

promise优势

1.指定回调函数的方式更加灵活

旧的方法需要在启动异步任务之前指定回调

promise在启动异步任务=>返会promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

2.支持链式调用,可以解决回调地狱的问题。

回调地狱:回调函数嵌套调用外部回调函数,异步执行的结果是嵌套的回调函数执行的条件。(层层嵌套的回调函数就是回调地狱)

回调地狱缺点是代码不便于阅读,也不便于异常的处理。

解决回调地狱的方式:then的链式调用和async/await (它底层其实还是使用的then的链式调用)

了解一下宏队列和微队列

宏队列:[宏任务1,宏任务2.....]

微队列:[微任务1,微任务2.....]

规则:每次要执行宏队列里的任务之前,先看微队列里是否有待执行的微任务。如果有,先执行微任务,如果没有,则按照宏队列里的任务顺序依次执行。

定时器所指定的回调属于宏任务,promise里的回调属于微任务,微队列的优先级比宏队列的优先级要高。

要注意的是只有promise里的回调是属于微任务。

小结

Promise 是现代 JavaScript 异步编程的基础。它避免了深度嵌套回调,使表达和理解异步操作序列变得更加容易,并且它们还支持一种类似于同步编程中 try...catch 语句的错误处理方式。

Promise 在所有现代浏览器的最新版本中都可以使用;唯一会出现支持问题的地方是 Opera Mini 和 IE11 及更早的版本。

MDN:如何使用 Promise - 学习 Web 开发 | MDN

学习参考:promise对象

以及学习:Promise - 廖雪峰的官方网站

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白芸哆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值