企业型网站怎么做,2017国外优秀网站模版,如何推广自己的公司,做网站专业术语我们在前面的文章《JavaScript 基础#xff1a;异步编程/单线程和异步》中讲过#xff0c;Javascript 是⼀⻔单线程语⾔。早期我们解决异步场景时#xff0c;⼤部分情况都是通过回调函数来进⾏。
#xff08;如果你还不了解单线程和异步的概念#xff0c;可以先去回顾上一…我们在前面的文章《JavaScript 基础异步编程/单线程和异步》中讲过Javascript 是⼀⻔单线程语⾔。早期我们解决异步场景时⼤部分情况都是通过回调函数来进⾏。
如果你还不了解单线程和异步的概念可以先去回顾上一篇文章。
回调函数的定义
把函数 A 传给另一个函数 B 调用那么函数 A 就是回调函数。
例如在浏览器中发送 ajax 网络请求或者在定时器中执行异步任务就是最常⻅的异步场景。发送请求后需要等待一段时间等服务端响应之后我们才能拿到结果。如果我们希望等待异步任务结束之后再执⾏想要的操作就只能通过回调函数这样的⽅式进⾏处理。 const dynamicFunc function (callback) {setTimeout(function () {console.log(一开始在这里执行异步任务 task1延迟3秒执行);// task1 total 计数let total 0;for (let i 0; i 10; i) {total i;}// 等待异步任务 task1 执行完成后通过回调传入的 callback() 函数通知外面的调用者可以开始做后续任务 task2 了// 如果有需要的话可以把 task1 的执行结果 total 传给外面。callback callback(total);}, 3000);};// 执行同步任务 task2。需要先等 异步任务 task1做完。dynamicFunc(function (value) {console.log(外面监听到异步任务 task1已经完成了并且还能拿到 task1的执行结果 value);console.log(task1的返回值value: value);// task2将task1的执行结果乘以2const result value * 2;console.log(result: result);});
上⾯的例⼦中dynamicFunc() 函数里面的 setTimeout()就是⼀个异步函数在里面执行了一些异步任务延迟3秒执行。dynamicFunc() 的参数 callback() 就是一个回调函数。这段代码的诉求是先等待 异步任务 task1 做完再做 同步任务task2。我们来分析一下。
已知异步任务 task1 需要3秒才能做完。3秒结束后通知 dynamicFunc 函数的调用者里面的异步任务 task1 已经做完了外面可以开始做后续的任务 task2 了。那要怎么通知呢在ES5中最常见的做法就是需要回调传入的 callback 函数也就是回调函数 通知外面的调用者。并且如果有需要的话外面还可以拿到异步任务task1的执行结果 total详见代码注释。
注callback这个单词并不是关键字可以自由命名我们通常习惯性地用“回调”这个词的英文名 callback 代表回调函数。
回调函数的异常处理
实际开发中为什么会经常存在异步任务呢这是因为有很多函数在执行时无法立即完成我们也不知道它什么时候能完成。但是我们需要等待它完成后才能做接下来的事情。换句话说我们接下来要做的事情需要依赖此前的异步任务。
比如 ajax 网络请求就是典型的异步任务。在渲染一个页面时我们需要请求接口获取页面所需要的数据。等接口请求完成、数据准备好之后前端就可以对数据进行处理并将数据渲染到页面了。前端做的这部分事情就是在回调函数里面做。
当然异步任务在执行时可能出现异常、错误信息、执行失败等等。当出现异常时往往导致后续的回调函数无法执行。这就需要在异步任务中将异常信息通知给外部。
代码举例如下 // 封装异步任务const dynamicFunc function (number, successCallback, failureCallback) {setTimeout(function () {console.log(一开始在这里执行异步任务 task1延迟3秒执行);let total 0;for (let i 0; i 10; i) {total i;}if (number 0) {// 异步任务执行成功successCallback successCallback(total);} else {// 异步任务执行鼠标failureCallback failureCallback(异步任务执行失败);}}, 3000);};// 执行异步任务等待 异步任务 执行完成后再执行回调函数。dynamicFunc(100,(value) {console.log(异步函数调用成功: value);// task2将task1的执行结果乘以2const result value * 2;console.log(result: result);},(err) {console.log(异步函数调用失败, err);});
处理异步任务的基本模型
我们以“发送网络请求”为例通过回调函数处理异步任务时既有请求成功的情况也有请求失败的情况。其基本处理模型如下
1调用一个异步函数在这个函数中发送网络请求也可以用定时器来模拟异步任务。
2如果网络请求成功则告知调用者请求成功并将接口返回的数据传递出去。
3 如果网络请求失败则告知调用者发送失败并将错误信息传递出去。
ES5中回调函数处理异步任务的基本代码结构如下 // ES5中使用传统的回调函数处理异步任务的基本模型// 封装异步任务function requestData(url, successCallback, failureCallback) {const res {retCode: 0,data: qiangu yihaos data,errMsg: network is error,};setTimeout(() {if (res.retCode 0) {// 网络请求成功successCallback(res.data);} else {// 网络请求失败failureCallback(res.errMsg);}}, 1000);}// 调用请求异步任务requestData(www.qianguyihao.com/xxx,// 成功监听res {console.log(异步任务执行成功:, res);},// 失败监听err {console.log(异步任务执行失败:, err);});
我们一定要记住这个处理模型它我们学习异步编程的范式之一。如果前端接下来要做的事情需要依赖这个异步任务、需要等待这个异步任务做完之后才能继续那就符合上面的处理模型。
ES5中回调的缺点异步代码的困境
上面的回调函数的写法都是ES5的写法。ES5中回调的写法比较直观不需要 return层层嵌套即可。但也存在两个问题 如果嵌套过深则会出现回调地狱的问题。 不同的异步函数回调的参数在写法上可能不一致导致不规范、且需要单独记忆。
我们来具体看看这两个问题。
1、回调地狱的问题
如果多个异步任务存在依赖关系比如需要等第一个异步任务执行完成后才能执行第二个异步函数等第二个异步任务执行完毕后才能执行第三个异步任务就需要多个异步任务进⾏层层嵌套⾮常不利于后续的维护而且会导致回调地狱callback hell的问题。
简而言之当一个回调函数嵌套另一个回调函数时就会出现一个嵌套结构。如果嵌套次数过多就会出现回调地狱的情况。像下面这样 关于回调地狱我们来举一个形象的例子 假设买菜、做饭、洗碗、倒厨余垃圾都是异步的。 但真实的场景中实际的操作流程是买菜成功之后才能开始做饭。做饭成功后才能开始洗碗。洗碗完成后 再倒厨余垃圾。这里的一系列动作就涉及到了多层嵌套调用也就是回调地狱。 关于回调地狱我们来看看几段代码举例。
1.1、定时器的代码举例回调地狱 setTimeout(function () {console.log(qiangu1);setTimeout(function () {console.log(qiangu2);setTimeout(function () {console.log(qiangu3);}, 3000);}, 2000);}, 1000);
1.2、Node.js 读取文件的代码举例回调地狱 fs.readFile(A, utf-8, function (err, data) {fs.readFile(B, utf-8, function (err, data) {fs.readFile(C, utf-8, function (err, data) {fs.readFile(D, utf-8, function (err, data) {console.log(qianguyihao: data);});});});});
上面代码的逻辑为先读取 A 文本内容再根据 A 文本内容读取 B然后再根据 B 的内容读取 C。为了实现这个业务逻辑上面的代码就很容易形成回调地狱。
1.3、ajax 请求的代码举例回调地狱 // 伪代码ajax(a.json, (res1) {console.log(res1);ajax(b.json, (res2) {console.log(res2);ajax(c.json, (res3) {console.log(res3);});});});
2、回调写法不一致的问题
我们需要自己去设计回调函数包括回调函数的参数格式 、调用方式等等。 // Node.js 读取文件时成功回调和失败回调是通过 error参数来区分readFile(d:\\readme.text, function (err, data) {if (error) {console.log(文件读取失败);} else {console.log(文件读取成功);}});// jQuery的 ajax 写法中成功回调和失败回调是通过两个回调函数来区分$.ajax({url: /ajax.json,success: function (response) {console.log(文件读取成功);},error: function (err) {console.log(文件读取失败);},});
我们可以看到上面的回调函数的代码中成功回调和失败回调参数的写法不一致。在实战开发中封装异步函数的人和调用异步函数的人往往不是同一个人。甚至可能出现的极端的情况是回调函数里需要传很多参数参数的顺序也不一致各有各的风格每个人写得都不一样。因为这种回调参数的写法不一致、不规范的问题所以需要单独记忆导致在调用时需要小心翼翼很容易出错。
小结
按照上面的分析在 ES5 中处理异步任务时产生的这两个问题ES6 中的 Promise 就可以解决。当然 Promise 的强大功能不止于此。我们去下一篇文章一探究竟。
赞赏作者
创作不易你的赞赏和认可是我更新的最大动力 希望各位可以点个赞点个关注这对up真的很重要谢谢大家啦