Promise对象和async异步函数

一.同步与异步介绍

同步行为对应内存中顺序执行的处理器指令。在程序执行的每一步,都可以推断出程序的状态。因此要访问一些高延迟的资源或者执行大量计算的代码,同步行为会导致线程阻塞。异步行为类似于系统中断,当执行到异步代码时,线程会同步往下执行,不会等待异步代码的执行结果。异步代码的执行结果,需要在一个合适的时机通知到其他代码,JavaScript经历数次迭代一直在寻找这个时机,以至于发展到现今的异步编程模式。

二.Promise对象

ES6新增的Promise类型为异步编程提供了良好的空间。一经推出,Promise就大受欢迎,成为了主导性的异步编程机制。所有现代浏览器都支持Promise类型,很多其他浏览器API(如fetch()和Battery Status API)也以Promise为基础。

1.对象介绍

Promise对象常用的方式是通过new来创建。参数是一个同步执行的执行器函数。该执行器函数接受2个function类型参数(reslove,reject)作为状态转变的回调函数。

let p = new Promise((resolve,reject) => {});

一个Promise对象的实例拥有3个状态

  • pending(等待)
  • resolved(解决)
  • rejected(拒绝)

刚创建的Promise实例一般是pending状态,也可以通过Promise的静态方法创建Promise.resolve()和Promise.reject()创建状态为resolved和rejected的Promise实例。通过new生成的Promise实例也可以调用执行器函数的function类型参数(resolve,reject)来改变Promise实例对象的状态。

2.使用方式

(1)Promise.prototype.then方法

let promise = new Promise((resolve,reject) => {
    //生成伪随机数
    let makeRandom = Math.random();
    if(makeRandom < 0.5){
        resolve(makeRandom)
    }else{
        reject(makeRandom)
    }
})
promise.then((resolveData)=>{
    console.log("resolveDate:" + resolveData);
},(rejectData) => {
    console.log("rejectDate:" + rejectData);
})
//伪随机数<0.5
//resolveDate:0.4353298933672951
//伪随机数>=0.5
//rejectDate:0.5901168630152123

当生成的伪随机数<=0.5时,执行器函数调用了resolve方法,当生成的伪随机数>0.5时,执行器函数调用了reject方法,并且都将生成的随机数当参数传入。在then方法里面分别调用了onResolve方法(第一个箭头函数)和onReject方法(第二个箭头函数)。因此当执行器函数里面执行了resolve或者reject方法来改变Promise实例对象的状态时,会触发then方法里面传入的对应的回调函数。

(2)Promise.prototype.catch方法

let promise = new Promise((resolve,reject) => {
    //生成伪随机数
    let makeRandom = Math.random();
    if(makeRandom < 0.5){
        resolve(makeRandom)
    }else{
        reject(makeRandom)
    }
})
promise.catch((rejectData) => {
    console.log("rejectDate:" + rejectData);
})
//伪随机数>=0.5
//rejectDate:0.8700442402383655

catch方法实际上是一个语法糖,它和then接口的第二个函数,即onReject方法没有任何区别。当伪随机数>=0.5时,调用reject方法将状态改为了rejected,会调用then接口的onReject方法或者catch方法来处理。

(3)Promise.prototype.finally方法

let promise = new Promise((resolve,reject) => {
    //生成伪随机数
    let makeRandom = Math.random();
    if(makeRandom < 0.5){
        resolve(makeRandom)
    }else{
        reject(makeRandom)
    }
})
promise.then((resolveData)=>{
    console.log("resolveDate:" + resolveData);
},(rejectData) => {
    console.log("rejectDate:" + rejectData);
})
promise.finally(() => {
    console.log("finally");
})

finally方法为Promise实例对象添加onFinally处理程序,这个处理程序在期约转换为解决或拒绝状态时都会执行。

3.链式调用

1.示例

let promise = new Promise((resolve,reject) => {
    //生成伪随机数
    let makeRandom = Math.random();
    if(makeRandom < 0.5){
        resolve(makeRandom)
    }else{
        reject(makeRandom)
    }
})
promise.then((resolveData)=>{
    console.log("resolveDate:" + resolveData);
    return resolveData + 1;
},(rejectData) => {
    console.log("rejectDate:" + rejectData);
    return rejectData - 1;
}).then((resolveData) => {
    console.log("resolveDate:" + resolveData);
    return Promise.reject(resolveData);
}).catch((catchData) => {
    console.log("catch:" + catchData);
}).finally(() => {
    console.log("finally");
})
//当生成伪随机数>=0.5时的打印结果
//rejectDate:0.9695780497440285
//resolveDate:-0.030421950255971497
//catch:-0.030421950255971497
//finally

//当生成伪随机数<0.5时的打印结果
//resolveDate:0.46144358778532846
//resolveDate:1.4614435877853285
//catch:1.4614435877853285
//finally

2.解析

then,catch和finally都会返回一个新的Promise实例对象,因此就可以进行链式调用。在上面示例中,第一个then方法不论是触发了onResolve还是onReject方法,最终都会返回一个resolved状态的Promise实例对象,解决值为显示返回的值,这个实例对象由于是resolved状态,因此会调用onResolve方法,而onResolve返回了一个rejected状态的Promise对象,该对象会调用catch方法传入的onReject方法处理,onReject方法返回一个新的Promise对象,由于没有明确返回值,所以会返回一个解决值为undefined的resolved状态Promise实例对象,这个实例对象由于状态为resolved,所以会调用onFinally处理函数。而onFinally也会返回一个Promise实例对象。具体的状态和上一个Promise实例对象保持一致。

4.Promise.all和Promise.race

Promise.all和Promise.race都接收一个Promise类型数组作为参数,并返回一个全新的期约。

(1)Promise.all

Promise.all()静态方法创建的Promise类型对象会在一组Promise实例对象全部解决后在解决。这个静态方法接收一个可迭代对象,返回一个新Promise类型对象,当一组Promise类型对象有一个状态为拒绝,则返回的Promise类型对象状态变为rejected。拒绝值为状态为rejected的Promise类型对象的拒绝值。当全部解决时,新Promise对象的解决值为所有Promise类型对象的解决值合并的数组。

let p = Promise.all([
  Promise.resolve(3),
  Promise.resolve(4),
  Promise.resolve(5)
]);

p.then((values) => setTimeout(console.log, 0, values)); // [3, 4, 5]

(2)Promise.race

Promise.race()静态方法返回一个Promise类型对象,是一组集合中最先解决或拒绝的Promise类型对象的镜像。

5.正确理解执行器函数的同步执行,异步回调

let promise = new Promise((resolve,reject) => {
    console.log("one");
    resolve();
})
promise.then(()=>{
    console.log("two");
})
console.log("three");
/*
one
three
two
*/

当执行执行器函数时,会打印"one"字符串,然后将状态变为resolved,此时并不会立即执行then方法里面传入的onResolve方法,而是将onResolve方法推入异步消息执行队列,然后继续执行同步代码,打印"three"字符串,在同步代码执行完之后,去执行异步消息队列里面的方法,此时,打印字符串"two"。

三.async异步函数

1.声明方式

async关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上。

async function foo() {}

let bar = async function() {};

let baz = async () => {};

class Qux {
  async qux() {}
}

async关键字声明的异步方法并不会异步执行,如果不和await关键字搭配使用,使用上和同步方法没有任何区别。

2.返回值

async声明的异步函数的返回值期待(但实际上并不要求)一个实现thenable接口的对象,但常规的值也可以。如果返回的是实现thenable接口的对象,则这个对象可以由提供给then()的处理程序“解包”。如果不是,则返回值就被当作resolved状态的Promise对象。

// 返回一个原始值
async function foo() {
  return 'foo';
}
foo().then(console.log);
// foo

// 返回一个没有实现thenable接口的对象
async function bar() {
  return ['bar'];
}
bar().then(console.log);
// ['bar']

// 返回一个实现了thenable接口的非期约对象
async function baz() {
  const thenable = {
    then(callback) { callback('baz'); }
  };
  return thenable;
}
baz().then(console.log);
// baz

// 返回一个期约
async function qux() {
  return Promise.resolve('qux');
}
qux().then(console.log);
// qux

拒绝期约的错误不会被异步函数捕获

async function foo() {
  console.log(1);
  Promise.reject(3);
}

// Attach a rejected handler to the returned promise
foo().catch(console.log);
console.log(2);

// 1
// 2
// Uncaught (in promise): 3

3.await关键字

await关键字只能存在于async声明的异步函数体内。await关键字期待(但实际上并不要求)一个实现thenable接口的对象,但常规的值也可以。如果是实现thenable接口的对象,则这个对象可以由await来“解包”。如果不是,则这个值就被当作resolved状态的Promise类型对象。

async function foo() {
  console.log(await 'foo');
}
foo();
// foo

// 等待一个没有实现thenable接口的对象
async function bar() {
  console.log(await ['bar']);
}
bar();
// ['bar']

// 等待一个实现了thenable接口的非期约对象
async function baz() {
  const thenable = {
    then(callback) { callback('baz'); }
  };
  console.log(await thenable);
}
baz();
// baz

// 等待一个期约
async function qux() {
  console.log(await Promise.resolve('qux'));
}
qux();
// qux

await会抛出错误的同步操作,会返回拒绝的期约

async function foo() {
  console.log(1);
  await (() => { throw 3; })();
}

// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log);
console.log(2);

// 1
// 2
// 3

单独Promise.reject()不会被异步函数捕获,而会抛出未捕获错误。不过,对拒绝的Promise实例对象使用await则会释放错误值,但后续代码不会执行。

async function foo() {
  console.log(1);
  await Promise.reject(3);
  console.log(4); // 这行代码不会执行
}

// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log);
console.log(2);

// 1
// 2
// 3

4.理解await关键字

要完全理解await关键字,必须知道它并非只是等待一个值可用那么简单。JavaScript运行时在碰到await关键字时,会记录在哪里暂停执行。等到await右边的值可用了,JavaScript运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。因此,即使await后面跟着一个立即可用的值,函数的其余部分也会被异步求值。

async function foo() {
  console.log(2);
  await null;
  console.log(4);
}

console.log(1);
foo();
console.log(3);

// 1
// 2
// 3
// 4

上述示例首先会同步执行输出1,然后执行异步函数foo,同步输出2,当JavaScript运行时碰到await时,向异步消息队列推送一个任务,然后接着同步执行输出3,在同步代码执行完成后,检查异步消息队列中的任务,发现null值立即可用,因此恢复执行await后面的代码,输出4。

四.温馨提示

以上说明理解都是个人总结,仅供参考。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值