Javascript异步功能简史

在学习Javascript时,我一遍又一遍地遇到了许多关于异步函数和操作的文章。 尽管具有此功能无疑具有优势,但每次作者引述的清单使我陷入困境。 话语改变了,本质保持不变,粥在我脑海中酝酿。 删减部分-ECMA历史发展和版本的小指南。

为什么我们需要异步操作?


计算机程序可以执行无限数量的任务。 Web应用程序必须处理许多不同的任务(通常需要使用相同的数据)已经不是什么秘密了。 特别是,最常见的示例之一是显示用户信息(UI)并使用服务器请求检索信息。 毫不奇怪,几乎每个Web开发人员都面临以下问题:处理给定的数据库,提供用户界面,组织一些API-所有这些实际上不仅在JS程序员的每个测试任务中。

为什么不按顺序执行命令?

通常,只有在相当长的一段时间之后,才能获得用户所需的信息。 如果将程序组织为:

  1. 从网站获取信息https:/ some / api / item / 1
  2. 在屏幕上显示有关第一项的信息。

在呈现页面并在用户上创建令人愉悦的印象时将产生严重的困难(所谓的用户体验)。 试想一下:一个页面,例如Netflix或Aliexpress,必须先从数百个数据库中获取数据,然后才能开始向用户显示内容。 这样的延迟将类似于加载3D游戏级别,并且如果玩家准备等待,则网站用户目前希望获取最多的信息。

找到了解决方案: 异步操作 。 在程序的主线程忙于在画布上初始化和显示网站元素时,它还本着“ 为用户获取商品 ”的精神将任务输出到其他线程。 一旦该线程完成工作,信息就会在主线程中“沉淀”并可供显示,并且在网页本身上就有一个特定的占位符-一个对象,该对象占用了将来的信息空间。

图片

此时,尽管某些请求尚未通过,但该页面已经显示。

图片

最有可能的是,在页面底部的某个地方,还有更多请求返回一个值,并且页面继续动态更新和呈现,而不会给用户带来任何不便。

ES5及更早版本:回调


在继续检查回调之前,让我们看一下/找出更高阶的函数是

JS中的高阶函数是将另一个函数作为参数的函数 。 这是一个例子:

objectIsString(objectRef) { return typeof(objectRef) === 'String'; } listOfObjects.filter(objectIsString); 

因此,函数objectIsString被传递给高阶函数filter-允许过滤listOfObjects并仅将字符串类型的对象保留在列表中。
回调以类似的方式工作。 这是一个作为参数传递给另一个函数的函数。 通常,setTimeout函数用作处理回调的函数的示例。 通常,它用作setTimeout(函数,超时值),其中函数是浏览器在超时指定的时间后执行的回调函数。

 setTimeout(console.log(1), 2000); console.log(2); 


打印2 1。

ES 6:承诺


在标准6中,引入了一种新类型-Promise(承诺,以下简称为Promise)。 承诺是一种类型,其对象具有以下三种状态之一:未决,已实现,已拒绝。 此外,使用最后两个状态,您可以“关联”函数-回调。 一旦在promise框架内描述的异步过程成功/失败,就会调用与之关联的功能。 此过程称为“挂起回调”,它使用promise本身的then和catch方法执行。 区别在于,当您随后调用参数时,将传递两个函数-成功(onFullfillment)和失败(onRejected),而catch接受(因为不难猜测)仅接受一个处理承诺中错误的函数。 为了确定在特定情况下是否成功执行了Promise,以及参数化了返回的结果

让我们分阶段创建和使用一个承诺。

 // : let promise; //     Promise. let promise = new Promise((resolve, reject) => { }); //  ,  . let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("result"); }, 1000); }); 

现在,使用then方法添加事件处理程序。 处理成功的函数的参数是结果,而处理承诺失败的函数的参数是错误。

 promise .then( result => { }, error => { } ); //     – . promise .then( result => { //  - -    resolve alert("Fulfilled: " + result); // result -  resolve }, error => { //   -    reject alert("Rejected: " + error); // error -  reject } ); 

做完了!

因此,我们将再次简要描述创建承诺的过程:

  1. 初始化对象(新的Promise)
  2. 我们将resolve和/或reject函数作为唯一参数传递给构造函数。 一个函数必须至少有1个异步操作
  3. 使用then / catch方法,我们添加函数-结果处理程序。

发电机。 产量


同样在ES6标准中,定义了一种新型的函数-生成器。 乍一看,这些函数可以通过相同的调用多次返回不同的值。 让我们看看他们如何做以及为什么使用它。

生成器的标准格式:function * functionName(){}。 在函数本身的主体中,单词yield用于返回中间值。

例如,考虑以下生成器:

 function* generateNumber() { yield 1; yield 2; return 3; } 

目前,生成器正在执行。 每次调用下一个生成器方法时,将执行在下一个yield(或return)之前描述的代码,并且还将返回这些单词之一所在行中指示的值。

 Let one = generateNumber.next(); // {value: 1, done: false} 

下次调用将以相同的方式返回值2,第三次调用将返回3值并结束函数。

 Let two = generateNumber.next(); // {value: 2, done: false} Let three = generateNumber.next(); // {value: 3, done: false} 

尽管如此,仍然可以通过下一个功能访问生成器。 但是,它将返回相同的值:{done:true}对象。

ES7。 异步/等待


希望借助语法糖类和继承的继承来取悦OOP爱好者,ES7的创建者正在努力促进对javascript的理解,并为爱好者编写同步代码。 使用async / await构造,用户可以编写与同步尽可能相似的异步代码。 如果需要,您可以摆脱最近研究的Promise,并以最小的更改重写代码。
考虑一个例子:

使用承诺:

 requestBook(id) { return bookAPIHelper.getBook(id).then(book => {console.log(book)}); } 

使用异步/等待。

 async requestBook(id) { Const book = await bookAPIHelper.getBook(id); Console.log(book); } 

让我们描述一下我们所看到的:

1)异步-在声明异步函数时添加了关键字
2)等待-调用异步函数时添加的关键字。

ES8。 异步迭代


早在ES5中就可以同步迭代数据。 经过两个规范,决定增加在异步数据源中进行异步迭代的可能性。 现在,当调用next()时,它不会返回{value,done},而是一个承诺(请参阅ES6)。

让我们看一下createAsyncIterable(可迭代)函数。

 async function* createAsyncIterable(iterable) { for (const elem of iterable) { yield elem; } } 

如您所见,该函数将初始化集合,每次调用元素时,都会使用在iterable中指定的值返回一个promise。

 const asyncIterable = createAsyncIterable(['async 1', 'async 2']); const asyncIterator = asyncIterable[Symbol.asyncIterator](); asyncIterator.next() .then(result => { console.log(result); // { // value: 'async 1', // done: false, // } return asyncIterator.next(); }) .then(result => { console.log(result); // { // value: 'async 2', // done: false, // } return asyncIterator.next(); }) .then(result => { console.log(result); // { // value: 'undefined', // done: true, // } }); 

此外,新标准定义了一个等待循环,这种循环很方便。

 for await (const x of createAsyncIterable(['a', 'b'])) 

TL; DR


根本不需要完全记住和记住该语法或哪个语法属于哪个版本的ECMAScript,尤其是当您刚开始接触JS中的异步行为时。 同时,按照规范的发​​展历史所提出的顺序来研究异步性,不仅使程序员可以完美地理解传递给JS引擎的语法和指令,而且可以遵循改进ECMAScript作为产品的逻辑,了解JS开发人员指示的趋势,将其分离并接受。

简而言之,则:

回调<= ES5
承诺,收益(生成器):ES6
异步/等待:ES7
异步迭代器:ES8

Source: https://habr.com/ru/post/zh-CN434140/


All Articles