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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。