自学内容网 自学内容网

Javascript异步编程

Why异步

 保证浏览器正常渲染不堵塞,不影响用户使用

 总体介绍

浏览器运行是多进程的,包括主进程、渲染进行、网络进程和GPU进程等等

解析HTML时(渲染进程)用到两大引擎,一个是渲染引擎(用于渲染页面)、一个是JS引擎用于解析JS代码。又JS引擎运行是单线程的,所以渲染和 js引擎是互斥的。这就要求提高JS引擎解析时间,防止长时间堵塞主线程,以提高渲染效率。异步就可用来解决此问题。

渲染引擎的线程运行

浏览器运行渲染进程先利用渲染引擎渲染页面,渲染过程如下:

 JS引擎的线程运行

主线程中的任务先进入任务队列中给JS引擎解析,其他线程的任务(优先级如下图)的以回调任务进行队列待JS引擎解析。若JS代码改变了DOM树,渲染任务又进入任务队列中待渲染引擎执行。

 主线程就监控检查任务队列中是否任务待执行,有就在主线程中执行,部分任务给其他线程运行(异步任务)【这些任务根据逻辑(如定时3s)将回调函数传到任务队列中,待主线程执行】

 异步编程实现

  • 回调函数

      回调函数是最早也是最基本的异步编程方式。它将需要等待的函数作为参数传递给另一个函数,当等待的操作完成时,再调用这个回调函数。但这种方式可能会导致“回调地狱”(Callback Hell),即多层嵌套的回调函数使得代码难以阅读和维护。

function doSomething(callback) {  
    setTimeout(() => {  
        console.log('Doing something...');  
        callback();  
    }, 1000);  
}  
  
doSomething(() => {  
    console.log('Done!');  
});
  1. 优点:简单、容易理解
  2. 缺点:不利于维护,代码耦合高
  • 事件监听(采用时间驱动模式,取决于某个事件是否发生):

 

在JavaScript中,事件监听是一种强大的机制,允许你监听和响应网页上发生的各种事件,如用户点击按钮、输入文本、页面加载完成等。通过为元素添加事件监听器,你可以指定当特定事件发生时应该执行的函数。

基本语法

向元素添加事件监听器的基本语法是使用addEventListener()方法。这个方法接受至少两个参数:要监听的事件类型(如clickloadmouseover等)和一个当事件发生时会被调用的函数(事件处理函数)。

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=

  1. 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
  2. 缺点:事件驱区动型,流程不够清晰
  • 发布/订阅(观察者模式)
  1. 类似于事件监听,但是可以通过‘消息中心',了解现在有多少发布者,多少订阅者
  • Promise对象

Promises是ES6中引入的,用于处理异步操作的对象。它代表了一个最终可能完成或失败的操作及其结果值。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

function doSomething() {  
    return new Promise((resolve, reject) => {  
        setTimeout(() => {  
            resolve('Done!');  
            // 或者在某些情况下使用 reject('Error!');  
        }, 1000);  
    });  
}  
  
doSomething().then(result => {  
    console.log(result);  
}).catch(error => {  
    console.error(error);  
});

Promise其实也不难-CSDN博客

  1. 优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数;
  2. 缺点:编写和理解,相对比较难
  • Generator函数

Generators是ES6中引入的,提供了一种执行暂停和恢复代码的机制。虽然它们本身不直接处理异步操作,但可以通过与Promises结合使用(例如通过co库),来实现异步编程。然而,由于async/await的引入,使得Generators在异步编程中的使用逐渐减少。

Generator 函数是 ES6 引入的一种异步编程解决方案,它允许你编写一个可以暂停执行和恢复执行的函数。这种函数可以通过 function* 声明(注意星号),并且可以使用 yield 关键字来暂停和恢复函数的执行。Generator 函数返回一个遍历器对象(Iterator),因此你可以使用 next() 方法来遍历 Generator 函数内部的每一个状态。

function* generatorFunction() {  
  yield 'Hello';  
  yield 'World';  
  return 'Ending';  
}  
  
const generator = generatorFunction();  
  
console.log(generator.next().value); // 'Hello'  
console.log(generator.next().value); // 'World'  
console.log(generator.next().value); // 'Ending'  
console.log(generator.next().done);  // true

yield 关键字

  • yield 表达式是暂停 Generator 函数执行的点。
  • 每次调用 next() 方法时,Generator 函数会从上次 yield 表达式停止的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)或函数末尾。
  • yield 可以返回任何值给 next() 方法的调用者。
  • 如果 yield 表达式后面没有跟值,则 next() 方法的 value 属性为 undefined

next() 方法

  • next() 方法可以接受一个参数,这个参数会被当作上一个 yield 表达式的返回值。
  • next() 方法返回一个对象,该对象包含两个属性:value 和 donevalue 属性表示 yield 表达式的值,done 属性是一个布尔值,表示 Generator 函数是否已经执行完毕。

应用场景

Generator 函数的主要用途之一是处理异步操作,尽管在现代 JavaScript 开发中,async/await 语法已经成为处理异步操作的首选方式。然而,Generator 函数仍然有其用武之地,特别是在需要手动控制异步流程或需要与旧代码库集成

 

function* fetchData() {  
  const data1 = yield fetch('https://api.example.com/data1');  
  const json1 = yield data1.json();  
  console.log(json1);  
  
  const data2 = yield fetch('https://api.example.com/data2');  
  const json2 = yield data2.json();  
  console.log(json2);  
}  
  
// 辅助函数,用于处理 Generator 函数中的 Promise  
function run(gen) {  
  const it = gen();  
  
  function handle(result) {  
    if (result.done) return result.value;  
    return result.value.then(data => handle(it.next(data)));  
  }  
  
  return handle(it.next());  
}  
  
run(fetchData);
  1. 优点:函数体内外的数据交换、错误处理机制
  2. 缺点:流程管理不方便
  • async函数

async/await是ES2017(ES8)中引入的,建立在Promises之上,使得异步代码看起来和同步代码一样。async函数会隐式返回一个Promise,而await关键字则用于等待Promise的解决(resolve)或拒绝(reject),并可以从中检索解决的值。

async function doSomething() {  
    try {  
        const result = await new Promise((resolve) => {  
            setTimeout(() => {  
                resolve('Done!');  
            }, 1000);  
        });  
        console.log(result);  
    } catch (error) {  
        console.error(error);  
    }  
}  
  
doSomething();
  1. 优点:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰。
  2. 缺点:错误处理机制

 


原文地址:https://blog.csdn.net/m0_55049655/article/details/140726716

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!