写点什么

js 异步编程的三种模式

作者:hellocoder2029
  • 2022-10-31
    浙江
  • 本文字数:1962 字

    阅读完需:约 6 分钟

写在前面

 javascript 语言的执行环境是"单线程"(single thread),就是指一次只能完成一件任务。如果有多个任务,就必须排队,等前面一个任务完成,再执行后面一个任务,以此类推。

 这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

单线程

function f1() {    console.log('1')}function f2() {    console.log('2')}f1()f2()
复制代码


 很容易可以看出,上述代码会依次输出 1,2。因为代码是从上到下,依次执行,执行完 f1(),才会执行 f2()。但是如果 f1()中的代码执行的是读取文件或者 ajax 操作呢,文件的读取都需要一定时间,难道我们需要完全等到文件完全读完再进行写操作么?为了解决这个问题,接下来我们来探究一下 js 中 同步和异步 的概念。

同步和异步

同步


  • 指在 主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。

  • 也就是调用一旦开始,必须这个调用 返回结果(划重点——)才能继续往后执行。程序的执行顺序和任务排列顺序是一致的。


异步


  • 异步任务是指不进入主线程,而进入 任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。

  • 每一个任务有一个或多个 回调函数。前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行。

  • 程序的执行顺序和任务的排列顺序是不一致的,异步的。

  • 我们常用的 setTimeout 和 setInterval 函数,Ajax 都是异步操作。


那么如何实现异步编程呢,笔者介绍几种方法

回调函数(Callback)

回调函数,这是异步编程最基本的方法。


const fs = require('fs')fs.readFile('./pakage.json',(err,info) => {    fs.writeFile('./p.json',info,(err) => {        if(!err) {            setTimeout(() => {                console.log('ok')            },2000)        }    })})
复制代码


上述代码通过回调函数的嵌套,从文件系统中读取一个./pakage.json 文件并写入./p.json,读取成功两秒后输出'ok'。用回调来实现异步,没有什么问题。


但是试想,如果再多几个异步函数,代码整体的维护性,可读性都变的极差,如果出了 bug,修复过程也变的极为困难,这个便是所谓的 回调函数地狱


参考视频讲解:进入学习

Promise 对象

Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。


MDN 对 Promise 定义如上,Promise 本意为承诺,我们可以理解为程序承诺过一段时间后会给你一个结果。Promise 是一个对象,可以保存三个状态 每一时刻必须有一个状态。


  • 成功 Fulfilled

  • 失败 Rejected

  • 处理中 Pending


  1. 默认 pending 如果调用 resolve fulfilled

  2. 默认 pending 如果调用 reject rejeced



const fs = require('fs')const promise1 = new Promise((resolve,reject) => {    fs.readFile('./package.json',(err,info) => {        resolve(info)    })})const promise2 = (info) => {    new Promise((resolve,reject) => {        fs.writeFile('./p.json', info,(err) => {            if(!err) {                resolve();            }else{                reject();            }        })    })}const promise3 = (time) => {    return new Promise((resolve,reject) => {        setTimeout(() => {            resolve()        },time)    })}//then链式调用//读文件成功 将结果作为参数传入promise2promise1.then((info) => {    return promise2(info)}).then(() => {    // 等着前面的promise     console.log('读写完成')    return promise3(2000)}).then( ()=> {    console.log('ok')})
复制代码


 这么一看,并没有什么区别,还比上面的异步回调复杂,得先新建 Promise 再定义其回调。但其实,Promise 的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。


 我们先使用new来构建一个promise。Promise 接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。


resolve:成功时调用,并将结果,作为参数传递出去;


reject:失败时调用,并将错误,作为参数抛出。


  • then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个 参数是 Promise 执行失败时的回调。

  • Promise 对象的 then 方法返回一个新的 Promise 对象,因此所以可以通过链式调用 then 方法。


我们还可以继续优化一丢丢。

async+await 语法糖

直接上代码


async function run() {    let info = await promise1;    await promise2(info);    await promise3(2000);    console.log('ok');}
复制代码


async 函数是在 ES2017 标准中引入的,使我们异步的代码更加优雅了。这里使用 async+await 代替了.then()方法。


  • async 必须在函数声明前

  • await 接一个 promise,那么后面的代码就会等待,等 promise resolve 了才会执行。


用户头像

还未添加个人签名 2022-09-08 加入

还未添加个人简介

评论

发布
暂无评论
js异步编程的三种模式_JavaScript_hellocoder2029_InfoQ写作社区