LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

【JavaScript】掌握异步编程,看这里!

admin
2024年3月29日 23:43 本文热度 497

异步处理的概念

JavaScript 中的异步处理指的是在代码执行过程中,能够不阻塞当前线程并处理一些时间较长的操作。异步处理通常涉及到回调函数、Promise、async/await 等机制。

在 JavaScript 中,传统的同步处理方式采用的是阻塞式的单线程模型。这种模型的缺点是当一个任务被执行时,它会一直执行到结束,期间如果有耗时的操作也会一直阻塞下去,直到任务执行完毕,才会执行后续的任务。这种方式会导致页面卡死,体验非常不好。

因此,JavaScript 异步处理机制应运而生,它允许在代码执行过程中,执行一些耗时的操作,而不会阻塞当前线程。

回调函数

回调函数是一种很常见的异步编程模型,通过在异步操作完成后调用回调函数来通知异步操作已结束,从而执行后续的任务。例如:

function fetchData(callback) {

  setTimeout(function() {

const data = { name: '张三', age: 20 };

    callback(data);

  }, 1000);

}


fetchData(function(data) {

  console.log(data);

});

在这个示例中,fetchData() 函数在完成数据加载后,调用回调函数 callback() 并传递数据作为参数。当数据加载完成后,控制器会跳转到回调函数中执行后续任务。

Promise

Promise 是一种比较流行的异步编程模型,它可以在异步操作完成后执行一些回调操作,并将结果返回给请求方。Promise 代表了一个异步操作的最终完成(或失败)及其结果值。例如:

function fetchData() {

  return new Promise(function(resolve, reject) {

    setTimeout(function() {

      const data = { name: '张三', age: 20 };

      resolve(data);

    }, 1000);

  });

}


fetchData().then(function(data) {

  console.log(data);

});

异步处理常见场景与处理策略

异步处理常见场景包括但不限于:

  1. 网络请求:当请求数据需要一定的时间才能返回时,为了避免用户体验受到影响,需要进行异步处理。
  2. 定时任务:定时执行任务,需要进行异步处理。
  3. 事件处理:通过异步处理来避免事件处理函数执行时间过长,导致页面卡顿等问题。

  4. 大量数据处理:对于大量数据的处理,需要进行异步处理,以免阻塞主线程。

JavaScript 引擎是单线程执行的,也就是说同一时间内只有一个任务在执行。当需要进行异步操作时,通常会使用回调函数。

假设我们有一个获取用户信息的异步函数 getUserInfo,在信息获取完成后需要调用相关回调函数。一种实现方式是将回调函数作为 getUserInfo 函数的第二个参数传入,信息获取完成后调用该函数。

function getUserInfo(userId, callback) {

  setTimeout(function() {

    const userInfo = {

      id: userId,

      name: "Tom",

      age: 25

    }

    callback(userInfo)

  }, 1000)

}


getUserInfo(1001, function(userInfo) {

  console.log(userInfo)

})

上述代码首先调用 getUserInfo 函数,该函数通过 setTimeout 模拟异步操作,等待 1 秒钟后获取用户信息,并在信息获取完成后调用传入的回调函数。最后在回调函数中输出用户信息。

Promise A+ 规范

Promise 的状态

一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)执行态(Fulfilled)和 拒绝态(Rejected)

  • 等待态(Pending)处于等待态时,promise 需满足以下条件:

    • 可以迁移至执行态或拒绝态(fulfill、reject)

  • 执行态(Fulfilled)处于执行态时,promise 需满足以下条件:

    • 不能迁移至其他任何状态

    • 必须拥有一个不可变的终值

  • 拒绝态(Rejected)处于拒绝态时,promise 需满足以下条件:

    • 不能迁移至其他任何状态

    • 必须拥有一个不可变的原因

    • 这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(译者注:概指当 value 或 reason 不是基本值时,只要求其引用地址相等,但属性值可被更改)。


      面试官文档 Promise A+ 规范时,首先要提出的便是三个态:pending、fulfilled、rejected


一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。

promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected);

其中,onFulfilled 和 onRejected 都是可选参数。

  • 如果 onFulfilled 不是函数,其必须被忽略

  • 如果 onRejected 不是函数,其必须被忽略

发布-订阅模式

根据 Promise A+ 规范,每次 then 返回的值也需要满足 thenable,也就是说我们需要将 resolve 返回值使用 promise 包裹,在本例中就是需要将返回值包装为新的 HePromise 对象。开发之前我们不妨先来看看 Promise 链式调用的示例:

const p = new Promise(resolve => resolve(1));

p.then(r1 => {

console.log(r1);

return 2;

})

  .then(r2 => {

console.log(r2);

return 3;

  })

  .then(r3 => {

console.log(r3);

  });

实现all方法

就是将传入数组中的值 promise 化,然后保证每个任务都处理后,最终 resolve。示例如下:

class HePromise {

static all(promises: any[]) {

let index = 0;

const result: any[] = [];

const pLen = promises.length;

return new HePromise((resolve, reject) => {

      promises.forEach(p => {

        HePromise.resolve(p).then(

val => {

            index++;

            result.push(val);

if (index === pLen) {

              resolve(result);

            }

          },

err => {

if (reject) reject(err);

          },

        );

      });

    });

  }

}

编写测试用例如下:

it('HePromise.all', done => {

  HePromise.all([1, 2, 3]).then(res => {

    expect(res).toEqual([1, 2, 3]);

    done();

  });

});

执行测试,测试通过。

实现race方法

就是将传入数组中的值 promise 化,只要其中一个任务完成,即可 resolve。示例如下:

class HePromise {

static race(promises: any[]): HePromise {

return new HePromise((resolve, reject) => {

      promises.forEach(p => {

        HePromise.resolve(p).then(

val => {

            resolve(val);

          },

err => {

if (reject) reject(err);

          },

        );

      });

    });

  }

}

编写测试用例:

it('HePromise.race', done => {

  HePromise.race([11, 22, 33]).then(res => {

    expect(res).toBe(11);

    done();

  });

});

执行测试,测试通过。

整体测试代码情况如下:

async 与 await 用法及原理详解

async function test() {

    const res = await Promise.resolve(1)

    return res

}

需要注意的是,使用 async、await 处理异步操作时,需要注意异常的处理。

异常处理

通常我们使用 try、catch 捕获 async、await 执行过程中抛出的异常,就像这样:

async function test() {

    let res = null

    try {

        const res = await Promise.resolve(1)

        return res

    } catch(e) {

        console.log(e)

    }

}

从零实现一个类似 async、await 的函数

promise+generator

function fn(nums) {

    return new Promise(resolve = >{

        setTimeout(() = >{

            resolve(nums * 2)

        },

        1000)

    })

}

function * gen() {

    const num1 = yield fn(1)

    const num2 = yield fn(num1)

    const num3 = yield fn(num2)

    return num3

}

function generatorToAsync(generatorFn) {

    return function() {

        return new Promise((resolve, reject) = >{

            const g = generatorFn() const next1 = g.next() next1.value.then(res1 = >{


                const next2 = g.next(res1) // 传入上次的res1

                next2.value.then(res2 = >{


                    const next3 = g.next(res2) // 传入上次的res2

                    next3.value.then(res3 = >{


                        // 传入上次的res3

                        resolve(g.next(res3).value)

                    })

                })

            })

        })

    }

}


const asyncFn = generatorToAsync(gen)

asyncFn().then(res = >console.log(res)) // 3秒后输出 8

自动执行

自动执行其实就是运用递归,将生成器函数产生的数据不断调用 next,直至执行完成。

function getData(endpoint) {

  return new Promise(resolve => {

    setTimeout(() => {

      resolve(`Data received from ${endpoint}`)

    }, 2000)

  })

}


// 生成器函数

function* getDataAsync() {

  const result1 = yield getData('Endpoint 1')

  console.log(result1)

  const result2 = yield getData('Endpoint 2')

  console.log(result2)

  return 'All data received'

}


// 将生成器函数包装成 Promise

function asyncToPromise(generatorFn) {

  const generator = generatorFn()


  function handleResult(result) {

    if (result.done) {

      return Promise.resolve(result.value)

    }


    return Promise.resolve(result.value)

      .then(res => handleResult(generator.next(res)))

      .catch(err => handleResult(generator.throw(err)))

  }


  try {

    return handleResult(generator.next())

  } catch (error) {

    return Promise.reject(error)

  }

}


asyncToPromise(getDataAsync).then(result => console.log(result))

总结

这里介绍了回调函数、Promise、async/await 等机制。其中涉及到JavaScript事件循环机制没有展开分析,后续会总结相应的通关手册。 


该文章在 2024/3/29 23:46:43 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved