js的promise和async、await的异步操作

一、异步为什么会发生和异步发生的场景:

https://blog.csdn.net/weixin_45745641/article/details/123997437?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%BC%82%E6%AD%A5%E5%9C%BA%E6%99%AF&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-123997437.142^v100^pc_search_result_base5&spm=1018.2226.3001.4187icon-default.png?t=N7T8https://blog.csdn.net/weixin_45745641/article/details/123997437?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%BC%82%E6%AD%A5%E5%9C%BA%E6%99%AF&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-4-123997437.142^v100^pc_search_result_base5&spm=1018.2226.3001.4187

二、回调函数

2.1回调函数是异步还是同步

回调函数可能是异步也可能是同步,同步回调是阻塞的,异步回调是非阻塞的。正常情况下回调函数是同步的,但当用settimeout和intervaltime函数,事件监听器等,回调函数就是异步的。

【异步回调是“非阻塞的”:高阶函数无需等待回调完成即可完成其执行。高阶函数可确保稍后在特定事件上执行回调。】

JavaScript中的回调函数看这篇就够了 - 疯狂的技术宅 - SegmentFault 思否

回调函数的缺点:

调函数容易串联,但是串联多的时候,不断的嵌套,会导致代码横向发展,难以理解和调试,被称为回调地狱。

代码:

//异步回调函数:
const text = () => {
	   document.write('hello james')
}
setTimeout(text,1000)

//同步回调函数
function A(callback) {
    console.log("A");
    callback();//函数A执行了函数B
}
function B() {
    console.log("B");
}
A(B);//函数B作为函数A的入参

三、promise

3.1 promise原理:


Promise是JavaScript中一种异步编程的解决方案,用于解决回调地狱和代码可读性差的问题。它是一个包装了异步操作的对象,可以让我们更优雅、清晰地处理异步操作。

3.2 Promise的优点


优化了异步操作的流程控制,避免了回调地狱和嵌套的回调函数。

可以更好地处理异步操作的状态和结果,使代码更加清晰、简洁。

可以使用Promise.all()方法和Promise.race()方法来处理多个异步操作的结果,进一步简化异步编程的过程。

3.3 Promise的缺点


对于初学者来说,可能需要一些时间来理解Promise的概念和使用方法。

需要在异步操作完成后手动调用resolve()或reject()方法,可能会出现遗漏或错误。

无法取消Promise,一旦创建就必须等待其状态发生变化。

在使用.then()方法链式调用多个Promise对象时,如果其中任意一个Promise对象发生错误,整个链式调用都会停止,需要使用.catch()方法来捕获错误。

3.4 Promise的状态


Promise对象有三种状态,分别为pending、resolved和rejected。Promise对象的状态只能从pending转变为resolved或rejected,一旦状态变化,就不会再改变。

3.4.1 pending


初始化状态,即Promise实例创建后的初始状态,既不是成功也不是失败状态。

3.4.2 fulfilled


操作成功的状态,这意味着Promise的一个操作已经成功完成。Promise对象的结果会传递给.then()方法注册的回调函数。

"resolved"可能是对"fulfilled"状态的一个普遍误解,因为一个"resolved"的Promise可能是"fulfilled"或者"rejected"。

3.4.3 rejected


意味着异步操作失败,Promise对象的错误会传递给.catch()方法注册的回调函数

3.5几个promise函数

Promise.all()

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

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

上面代码中,Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

下面是一个具体的例子。

// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});

上面代码中,promises是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。

下面是另一个例子。

const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

Promise.all([
  booksPromise,
  userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));

上面代码中,booksPromiseuserPromise是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommendations这个回调函数。

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法。

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

上面代码中,p1resolvedp2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

如果p2没有自己的catch方法,就会调用Promise.all()catch方法。

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。

下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

Promise.any()

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

Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {  // 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
});

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

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

下面是Promise()await命令结合使用的例子。

const promises = [
  fetch('/endpoint-a').then(() => 'a'),
  fetch('/endpoint-b').then(() => 'b'),
  fetch('/endpoint-c').then(() => 'c'),
];

try {
  const first = await Promise.any(promises);
  console.log(first);
} catch (error) {
  console.log(error);
}

上面代码中,Promise.any()方法的参数数组包含三个 Promise 操作。其中只要有一个变成fulfilledPromise.any()返回的 Promise 对象就变成fulfilled。如果所有三个操作都变成rejected,那么await命令就会抛出错误。

Promise.any()抛出的错误是一个 AggregateError 实例(详见《对象的扩展》一章),这个 AggregateError 实例对象的errors属性是一个数组,包含了所有成员的错误。

下面是一个例子。

var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);

Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
  console.log(result); // 42
});

Promise.any([rejected, alsoRejected]).catch(function (results) {
  console.log(results instanceof AggregateError); // true
  console.log(results.errors); // [-1, Infinity]
});

代码

//promise的语法

let p = new Promise((resolve,reject) => {
    console.log("promise本身是同步");
    reject("catch是异步");
}).then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err);
})
console.log("想不到吧")


//promise本身是同步,then才是异步

let p = new Promise((resolve,reject) => {
    console.log("promise本身是同步");
    resolve("then是异步");
}).then((res) => {
    console.log(res);
})
console.log("想不到吧");



//promise为了解决异步,避免回调地狱的方法是then

function testP(val) {
    return new Promise((resolve, reject) => {
       resolve(val);
    });
}
testP("0").then(res1 => {
    console.log(res1); //输出0
    return testP("1");
}).then(res2 => {
    console.log(res2); //输出1
    return testP("2");
}).then(res3 => {
    console.log(res3); //输出2
    return testP("3");
}).catch(err => {
    console.log(err);
});

创建promise对象时出现了new关键字,就明白了Promise对象其实就是一个构造函数,是用来生成Promise实例的。能看出来构造函数接收了一个函数作为参数,该函数就是Promise构造函数的回调函数,该函数中有两个参数resolve和reject,这两个参数也分别是两个函数!

简单的去理解的话resolve函数的目的是将Promise对象状态变成成功状态,在异步操作成功时调用,将异步操作的结果,作为参数传递出去。reject函数的目的是将Promise对象的状态变成失败状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

then方法中返回的也是一个promise示例

四、async和await

4.1 async和promise的关系

  • async/await 是消灭异步回调的终极武器
  • 但和Promise并不排斥,两者相辅相成
  • 执行 async 函数,返回的是 Promsie 对象
  • await 相当于 Promise 的 then ,then指的是成功,不指失败
  • try…catch 可捕获异常,代替了 Promise的 catch

js四种异步方法(回调函数、Promise、Generator、async/await)_js 异步函数-CSDN博客

async 和 await(详解)_async和await详解-CSDN博客

async/await最详细的讲解_async await-CSDN博客

async/await最详细的讲解_async await-CSDN博客

代码:

//async

async function test(){
   return 'hello async';
 }
 test().then((val) => {
   console.log(val);  //hello async 
 })

//await

/*
 * 传入参数n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n+200,这个值将用于下一步骤
*/  
function takeLongTime(n){
  return new Promise((resolve) => {
    setTimeout(() => resolve(n + 200),n);
  })
}
function step1(n){
  console.log(`step1 with ${n}`);
  return takeLongTime(n);
}
function step2(n){
  console.log(`step2 with ${n}`);
  return takeLongTime(n);
}
function step3(n){
  console.log(`step3 with ${n}`);
  return takeLongTime(n);
}

async function doIt() {
  console.time('doIt');
  let time1 = 300;
  let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
  let time3 = await step1(time2);
  let result = await step1(time3);
  console.log(`result is ${result}`);
  console.timeEnd('doIt');
}
 
doIt();
 
//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1512.904296875ms
  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值