前端学习:异步编程(Promise和异步函数)

本文介绍了JavaScript中的异步编程,包括JS的单线程和异步编程模型,阐述了回调地狱问题以及Promise如何解决这一问题。详细讲解了Promise的状态、实例方法、链式调用和静态方法如Promise.all()和Promise.race()。此外,还讨论了异步函数(async/await)的应用,如实现sleep()函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章旨在梳理JavaScript异步编程知识点,大多引用自《JavaScript高级程序设计》(第4版)。

异步编程

JS的单线程和异步编程

为什么javaScript是单线程的,却可以处理异步函数呢?
这个问题的答案很简单,JS是单线程事件循环模型,所有的同步任务放在主线程(或称为同步线程)的执行栈里,按照先进先出的顺序在主线程执行,当出现一个异步任务时,浏览器创建一个异步线程来执行异步函数,并将回调函数放入任务列表中,当主线程执行完当前执行栈中的任务后,再顺序执行回调函数任务列表里的任务。
因此虽然JavaScript是单线程的,但异步任务仍然是多线程完成的,且异步任务的回调函数又是由主线程来完成的。

回调地狱

以往的JS中,支持定义回调函数的方式来表示异步函数的完成,此时如果想要串联多个异步操作,就要在回调函数里嵌套异步任务再嵌套回调函数。。。从而产生了回调地狱。
回调地狱的解决方法——ES6的新引用类型:Promise类型

Promise

什么是Promise?Promise有哪些状态?

  • Promise是ES6的新引用类型,可以更加优雅的组织异步逻辑。
  • Promise有三种状态:pending、resolved和rejected。
  • Promise的状态不可逆,Promise的落定状态一旦确定,就是不可逆转的。
  • Promise的两大用途:
    (1)抽象的表示一个异步操作,Promise的状态表示异步操作是否完成。即告诉我们一个异步操作是否完成。
    (2)Promise封装的异步操作会实际生成某个值,这个值可以被我们访问到。

Promise有哪些实例方法?(then、catch、finally)

  • 为什么Promise可以使用then来实现链式调用?
    then方法实现了一个Thenable接口,每个then方法返回一个新的Promise实例,这个新的Promise又可以继续调用then方法。
  • then方法放入的两个参数,第一个是onResolved处理程序,第二个是onRejected处理程序,两个参数互斥。
  • onRejected处理程序用于捕获异步错误的任务,由于捕获错误成功(任务执行成功),那么它返回一个resolved的Promise。(注意onResolved处理程序抛出异常则返回一个rejected的Promise,但却会把错误对象包装在一个resolved的Promise中返回)
  • catch方法放入的是onRejected处理程序。
  • finally方法不管Promise的状态是resolved还是rejected都会执行,一般用于添加清理代码。

非重入特性

当Promise的状态确定以后,与它相关的处理程序不会立即执行,而是放到之前所说的任务队列里,等待主线程执行完所有目前的同步任务后才会执行(会按照添加他们的顺序依次执行)。

Promise的链式调用

  • Promise的链式调用目的是串联化异步任务,原理是每一个后续处理程序都会等待前一个Promise解决,然后实例化一个新的Promise并返回。
  • Promise的链式调用解决了回调地狱的问题。

Promise的静态方法:Promise.all()、Promise.race()

Promise.all()

  • Promise.all()接收一个可迭代对象,返回一个新的Promise(注意即使所有期约都成功也返回的是一个Promise,这个新的Promise的解决值是包含所有Promise解决值的数组)。
  • 如果至少有一个包含的Promise待定,那么合成的Promise也待定(pending),如果有一个Promise拒绝,那合成的Promise也拒绝(rejected)
  • 只有所有Promise都成功解决,那合成期约的解决值是包含所有Promise解决值的数组(数组顺序按照迭代器的顺序)
  • 如果有Promise拒绝,那么第一个拒绝的Promise会将自己的拒绝理由作为合成期约的拒绝理由。

Promise.race()

  • Promise.race()接收一个可迭代对象,返回一个新的Promise。
  • 返回一组集合中最先解决或拒绝(即最先落定)的Promise的镜像。
  • 不管是all还是race,合成期约会静默处理所有包含期约的拒绝操作,不会有错误跑掉。

Promise的应用

函数合成

将多个函数合成一个函数,利用链式调用的参数传递。

Promise的封装(实现一个Promisify函数)

将原本需要通过传入回调参数来实现回调执行(或者叫同步执行)改为利用promise的.then的方式来调用,从而实现逻辑上的同步操作。
重点:参数传递

function promisify(func) {
  return function (...args) {
    return new Promise( (resolve, reject) => {
      let callback = function(...args) {
        resolve(args)
      }
      // 给func函数主动塞入一个callback,这样在func中调用callback的时候实际执行的时候就是
      // 我们这里定义的callback函数,然后在我们的callback中调用resolve,
      // 这样一来,本来想要通过回调执行的操作就可以放在then函数中进行执行了
      func.apply(null, [...args, callback])
    })
  }
}

参考来源:https://juejin.cn/post/6844904024928419848

顺序打印1…9

//同步任务和异步任务的执行顺序问题,顺序输出0-9的方法
for(var i = 0; i < 10 ; i++){
	setTimeout(function(){
		console.log(i);
	});
}

//解决办法
//1.利用块级作用域
for(let i = 0; i < 10; i++){
	setTimeout(function(){
		console.log(i);
	},1000);
}

for (var i = 0; i < 10; i++) {
  setTimeout(console.log, 1000, i)
}
//"2.利用promise"
for(var i = 0; i < 10; i++){
	new Promise((resolve,reject) => {
		var j = i;//同步执行
		setTimeout(function(){
			console.log(j);
		},);
	})
}
//利用async函数
async function init(){
	for(var i = 0; i < 10; i++){
		await myPromise(i);
	}
}
function myPromise(i){
	return new Promise((resolve) =>{
		setTimeout(function(){
			console.log(i);
			resolve(true);
		}, 1000);
	})
}
init();
//闭包
 for(var i = 0; i < 10 ; i++){
 	(function(i){
 		setTimeout(function(){
 			console.log(i);
 		});
	})(i);
 }

异步函数(async/await)

应用

实现sleep()

//实现sleep
async function sleep(delay){
	return new Promise((resolve) => {
		setTimeout(resolve, delay);
	})
}
async function fn(){
	const t = Date.now();
	await sleep(5000);
	console.log(Date.now() - t);
}
fn();//5006
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值