javascript异步回调函数,js同步回调和异步回调

  javascript异步回调函数,js同步回调和异步回调

  这篇文章给大家带来了一些关于javascript的知识,主要介绍了JavaScript中异步和回调的基本概念,以及回调地狱的现象。本文主要介绍异步和回调的基本概念,这是JavaScript的核心内容。让我们模糊地看一看它们。希望对你有帮助。

  【相关推荐:javascript视频教程,web前端】

  

JavaScript异步与回调

  

一、前言

  在学习本文内容之前,我们必须先了解异步的概念。首先要强调的是异步和并行有着本质的区别

  并行,一般指并行计算,是指同时执行多条指令。这些指令可以在同一CPU的多个核心上执行,或者在多个CPU上执行,或者在多个物理主机上执行,甚至在多个网络上执行。同步一般是指按照预定的顺序执行任务,上一个任务完成后才能执行下一个任务。异步,对应同步,是指让CPU暂时搁置当前任务,先处理下一个任务,收到前一个任务的回调通知后,再返回前一个任务继续执行,整个过程无需第二个线程参与。或许用图片的方式来解释并行、同步、异步更直观。假设现在有两个任务A和B需要处理,那么并行、同步和异步的处理方法将被实现,如下图所示:

  

二、异步函数

   JavaScript为我们提供了很多异步函数,让我们可以方便地执行异步任务。也就是说,我们现在开始执行一个任务(功能),但是任务会在以后完成,具体完成时间不清楚。

  比如setTimeout函数就是典型的异步函数,fs.readFile和fs.writeFile也是异步函数。

  我们可以自己定义一个异步任务的案例,比如定制一个文件复制函数copyFile(from,to):

  const fs=require(fs )

  函数复制文件(从,到){

  fs.readFile(from,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  fs.writeFile(to,data,(err)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console.log(“复制完成”)

  })

  })

  } copyFile函数首先从参数from读取文件数据,然后将数据写入参数to所指向的文件。

  我们可以这样调用copyFile:

  CopyFile(。/from.txt“,”。/to . txt )//复制文件。如果此时copyFile后面有其他代码(.),那么程序不会等待Copyfile执行结束,而是直接执行下来。当文件复制任务结束时,程序并不关心。

  copyFile(。/from.txt“,”。/to.txt )

  //下面的代码不会等待上面代码的执行完成。

  .是在这里执行的,好像一切都还正常。但是,如果我们直接访问文件中的内容,会发生什么呢?/to.txt在copyFile(.)功能?

  这样不会读取复制的内容,就这样:

  copyFile(。/from.txt“,”。/to.txt )

  fs.readFile(。/to.txt ,(err,data)={

  .

  })如果。/to.txt文件在执行程序前没有被创建,你会得到如下错误:

  即使。/to.txt存在,无法读取其中复制的内容。

  出现这种现象的原因是:copyFile(.)是异步执行的。在程序被执行到拷贝文件(.)函数,它不等待其复制完成,而是直接向下执行,导致文件。/to.txt不存在或文件内容为空(如果文件是预先创建的)。

  

三、回调函数

  无法确定异步函数的具体执行结束时间。例如,readFile(from,to)函数的执行结束时间取决于文件from的大小。

  那么,问题是,我们怎样才能准确定位copyFile执行的终点,从而读取to文件的内容呢?

  这就需要回调函数,我们可以修改copyFile函数如下:

  函数copyFile(from,to,callback) {

  fs.readFile(from,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  fs.writeFile(to,data,(err)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console.log(“复制完成”)

  Callback()//复制操作完成后调用回调函数。

  })

  })

  }这样,如果我们需要在文件复制完成后立即执行一些操作,我们可以将这些操作写入回调函数:

  函数copyFile(from,to,callback) {

  fs.readFile(from,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  fs.writeFile(to,data,(err)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console.log(“复制完成”)

  Callback()//复制操作完成后调用回调函数。

  })

  })

  }

  copyFile(。/from.txt“,”。/to.txt ,function () {

  //传入一个回调函数,读取“to.txt”文件的内容并输出

  fs.readFile(。/to.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console.log(data.toString())

  })

  })如果,您已经准备好了。/from.txt文件,那么上面的代码可以直接运行:

  这种编程方式称为“基于回调”的异步编程风格,异步执行的函数要提供一个回调参数,在任务完成后调用。

  这种风格在JavaScript编程中很常见。例如,文件读取函数fs.readFile和fs.writeFile是异步函数。

  

四、回调的回调

  回调函数可以准确处理异步工作完成后的后续事宜。如果需要依次执行多个异步操作,就需要嵌套回调函数。

  场景:依次读取文件A和文件B

  代码实现:

  fs.readFile(。/A.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console . log( read file A: data . tostring())

  fs.readFile(。/B.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  console . log( read file B: data . tostring())

  })

  })执行效果:

  通过回调的方式,可以在读取文件a后立即读取文件B。

  如果我们想在文件B之后继续读取文件C呢?您需要继续嵌套回调:

  Fs.readfile(。/a.txt ,(err,data)={//第一次回调

  如果(错误){

  console.log(错误消息)

  返回

  }

  console . log( read file A: data . tostring())

  Fs.readfile(。/b.txt ,(err,data)={//第二次回调

  如果(错误){

  console.log(错误消息)

  返回

  }

  console . log( read file B: data . tostring())

  Fs.readfile(。/c.txt ,(err,data)={//第三次回调

  .

  })

  })

  })也就是说,如果要依次执行多个异步操作,就需要多个嵌套的回调,在层数较少的情况下是有效的,但是嵌套次数过多就会出现一些问题。

  回调的约定

  实际上,fs.readFile中回调函数的样式并不是一个例子,而是JavaScript中的一个通用约定。我们以后会定制大量的回调函数,也需要遵守这个约定,形成良好的编码习惯。

  协议是:

  回调的第一个参数是为错误保留的。一旦出现错误,将调用回调函数(err)。第二个和后续参数用于接收异步操作的成功结果。此时,回调(空,结果1,结果2,)会被调用。基于上述约定,回调函数有两个功能:错误处理和结果接收。比如fs.readfile的回调函数( . ),(err,data)={})遵循这个约定。

  

五、回调地狱

  如果不深究的话,基于回调的异步方法处理似乎是相当完美的处理方法。问题是,如果我们有一个接一个的异步行为,那么代码看起来会像这样:

  fs.readFile(。/a.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  //读取结果操作

  fs.readFile(。/b.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  //读取结果操作

  fs.readFile(。/c.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  //读取结果操作

  fs.readFile(。/d.txt ,(err,data)={

  如果(错误){

  console.log(错误消息)

  返回

  }

  .

  })

  })

  })

  })上面代码的执行内容是:

  读取文件a.txt,如果没有错误;读取文件b.txt,如果没有错误;读取文件c.txt,如果没有错误;读取文件d.txt,……随着调用的增加,代码的嵌套层次越来越深,包含的条件语句越来越多,从而形成了不断向右缩进的混乱代码,难以阅读和维护。

  我们把这种不断向右增长(向右缩进)的现象叫做“回调地狱”或者“末日金字塔”!

  fs.readFile(a.txt ,(err,data)={

  fs.readFile(b.txt ,(err,data)={

  fs.readFile(c.txt ,(err,data)={

  fs.readFile(d.txt ,(err,data)={

  fs.readFile(e.txt ,(err,data)={

  fs.readFile(f.txt ,(err,data)={

  fs.readFile(g.txt ,(err,data)={

  fs.readFile(h.txt ,(err,data)={

  .

  /*

  通往地狱的大门。

  ===

  */

  })

  })

  })

  })

  })

  })

  })

  })虽然上面的代码看起来相当有规律,但这只是一个理想场景的举例。通常情况下,业务逻辑中存在大量的条件语句、数据处理操作等代码,从而打乱了当前美好的秩序,使得代码难以维护。

  幸运的是,JavaScript为我们提供了许多解决方案,Promise是最好的一个。

  【相关推荐:javascript视频教程,web前端】以上是JavaScript中异步和回调的基本概念以及回调地狱现象的细节。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: