JavaScript:异步编程模型

文章目录

  • 回调函数:
  • Promise:
  • Generator(生成器):
  • Iterator(迭代器):
  • async/await:
  • 异步迭代器(Asynchronous Iterators) 和 异步生成器(Async Generators)

JavaScript中的异步编程模型经历了几个发展阶段,从最初的回调函数到Promise,再到Generator和async/await

回调函数:

发布时间:JavaScript诞生之初就已经有了回调函数这种异步编程模型。

最初,JavaScript使用回调函数处理异步操作,如处理网络请求、定时器等。
当异步操作完成后,预先定义好的回调函数会被调用,通常作为参数传递给执行异步操作的函数。

function getData(callback) {
  setTimeout(() => {
    let data = 'Some data';
    callback(data);
  }, 1000);
}

getData((data) => {
  console.log(data); // 'Some data',在1秒后异步打印
});

Promise:

发布时间:ES6(ECMAScript 2015)

Promise是ES6引入的标准化解决方案,用于处理异步操作。Promise对象代表一个异步操作的最终完成(resolve)或失败(reject),它提供了链式调用和错误处理机制,解决了回调地狱的问题。

function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      let data = 'Some data';
      resolve(data);
    }, 1000);
  });
}

getData().then(data => {
  console.log(data); // 'Some data',在1秒后异步打印
});

Generator(生成器):

发布时间:ES6(ECMAScript 2015)

ES6中的Generator是一种迭代器协议的实现,它允许函数暂停执行和恢复执行,从而支持异步流程控制。配合yield关键字和第三方库(如co模块)可以实现异步操作的同步化书写风格。

生成器是一种特殊的迭代器,但不同于普通迭代器由类或工厂函数创建,生成器是由生成器函数生成的。
生成器函数通过在函数声明前放置星号(*)来定义,例如function* generatorFunc() {…}。
在生成器函数内部,可以使用yield关键字暂停函数的执行并产出一个值。每次调用生成器对象的next()方法时,函数会在上次暂停的地方继续执行直到遇到下一个yield表达式。
生成器的强大之处在于它可以控制迭代过程,不仅可以产出值,还可以接收外部传入的值(通过调用next(value)时传入的参数),从而实现双向通信。

function* asyncData() {
  let data = yield fetchSomeDataAsync();
  console.log(data);
}

function fetchSomeDataAsync() {
  return new Promise(resolve => {
    setTimeout(() => resolve('Some data'), 1000);
  });
}

// 需要配合第三方库或async/await实现异步执行
co(asyncData()); // 使用co模块执行生成器

注意:在实际项目中,由于现代JavaScript环境普遍支持async/await,因此直接使用async/await编写异步代码更为常见和简洁。而在过去,当async/await还未被广泛支持时,co库曾被广泛应用于处理生成器函数的异步流程控制。而现在,除非特殊场景或兼容旧代码,一般很少会再特意使用co库。

尽管 async/await 成为主流,但这并不意味着生成器失去了价值。在一些特定场景下,比如复杂的迭代器逻辑或者需要细粒度控制异步流程时,生成器依然有用武之地。

生成器还可以用于协程(coroutines)、流处理等场景,特别是在一些库和框架中,如 Koa.js(早期版本)中作为中间件的核心技术,即使现在许多新项目也倾向于迁移到使用 async/await

Iterator(迭代器):

// 创建一个简单数组
	const array = [1, 2, 3, 4, 5];
	
	// 自定义迭代器类
	class ArrayIterator {
	  constructor(arr) {
	    this.index = 0;
	    this.array = arr;
	  }
	
	  // 实现Symbol.iterator方法
	  [Symbol.iterator]() {
	    return this;
	  }
	
	  // 定义next方法
	  next() {
	    if (this.index < this.array.length) {
	      return { value: this.array[this.index++], done: false };
	    } else {
	      return { done: true }; // 没有更多元素了
	    }
	  }
	}
	// 使用方法一:使用for...of循环遍历
	for (const value of iteratorArray) {
	  console.log(value);
	}
	//使用方法一:手动迭代
	let iteration = iterator.next();
	while (!iteration.done) {
		console.log(iteration.value);
		iteration = iterator.next();
	}

在JavaScript中,许多内置对象已经内置了迭代器,可以直接用于for…of循环或其他迭代操作。
比如:数组 (Array)、字符串 (String)、Set 和 Map、TypedArray (如 Uint8Array, Int16Array 等)、NodeList(DOM API中的节点列表)

const array = [1, 2, 3];
const str = "hello";
const map = new Map([[1, 'one'], [2, 'two']]);
const set = new Set([1, 2, 3]);
const typedArray = new Uint8Array([1, 2, 3]);
const divs = document.querySelectorAll('div');
//下面【divs】替换成以上其中一个变量,都可遍历
for (const div of divs) {
  console.log(div);
}

async/await:

发布时间:ES8(ECMAScript 2017)

ES7引入了async函数和await关键字,它们是基于Promise的更高级抽象,使异步代码看起来更接近同步代码,大大提升了代码可读性和易用性。

async 表示这是一个async函数, await只能用在async函数里面,不能单独使用
async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行
await 等待的是一个Promise对象,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值

async function getData() {
  let data = await fetchSomeDataAsync();
  console.log(data);
}

async function fetchSomeDataAsync() {
  return new Promise(resolve => {
    setTimeout(() => resolve('Some data'), 1000);
  });
}

getData(); // 使用await关键字等待异步操作完成

异步迭代器(Asynchronous Iterators) 和 异步生成器(Async Generators)

发布时间:ES2018(ECMAScript 2018)

异步迭代器和异步生成器是迭代器和生成器的异步版本,用于处理异步生成的值序列,如从数据库查询记录等。

异步迭代器和异步生成器的区别在于它们处理异步值的能力。
异步迭代器通过实现 Symbol.asyncIterator 协议来返回一个异步迭代器对象。

而异步生成器则是可以包含 await 表达式的特殊生成器函数,它能够暂停并在异步操作完成时恢复执行。

// 异步生成器函数
async function* asyncGenerator() {
  // 模拟异步操作
  await new Promise(resolve => setTimeout(resolve, 1000));
  yield 'First value (after 1s)';

  await new Promise(resolve => setTimeout(resolve, 500));
  yield 'Second value (after another 0.5s)';
}

// 使用异步迭代器
(async () => {
  const ag = asyncGenerator();
  for await (const value of ag) {
    console.log(value);
  }
})();

总结起来,从ES6开始,JavaScript的异步编程模型得到了极大的丰富和完善,目前async/await是主流的异步编程方式

你可能感兴趣的:(JavaScript基础,javascript,开发语言,ecmascript)