栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > Web开发 > JavaScript

JavaScript异步编程之回调函数

JavaScript 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaScript异步编程之回调函数

基本概念

当一个函数满足以下两点时,就可以称之为回调函数(callback )。

1、这个函数作为参数传递给另外一个函数。
2、这个函数会在未来某个时间点(条件得到满足时或执行流到达时)被调用。

例子:

(function(fn) {
    let n = Math.floor(Math.random() * 10 + 1);
    let _fn = fn();
    if (n < 5) {
 _fn.done();
    } else {
 _fn.fail();
    }
})(callback);

function callback() {
    return {
 done() {
     console.log("小于5");
 },
 fail() {
     console.log("大于5");
 }
    }
}
回调函数的异步模式

回调函数本身并没有同步和异步之分,同步或异步主要取决于发布回调函数的任务源是什么。例如事件、Ajax(XMLHttpRequest)、setTimeout / setInterval 和 Promise 本身就是异步任务源,因此回调函数也被异步调用。如果回调函数是放在一段程序的最后去执行,那是利用了Javascript
本身的单线程缘故,属于同步任务。

例子:

同步执行回调函数

console.log(1);

(function(callback) {
    console.log(2);
    callback();
    console.log(3);
})(fn);

console.log(4);

function fn() {
    let arr = Array(1000000);
    arr.fill(1);
    console.log(arr.reduce((a, b) => a + b));
}
//输出: 1 2 1000000 3 4

异步执行回调函数

console.log(1);

(function(callback) {
    console.log(2);
    setTimeout(() => {
 callback();
    }, 0);
    console.log(3);
})(fn);

console.log(4);

function fn() {
    let arr = Array(1000000);
    arr.fill(1);
    console.log(arr.reduce((a, b) => a + b));
}
//输出: 1 2 3 4 1000000 
回调函数的意义

回调函数在形式上显然属于高阶函数的范畴,高阶函数的特点之一就是分解单个函数的复杂度,增强代码的可复用性。为了达到这个目的,我们往往需要抽离出那些容易变化的业务逻辑,把这部分业务逻辑放在回调函数中,这样一来可以分离业务代码中变化与不变的部分,增强了原函数的可复用性。
例如,我们想在页面上创建一组有序(顺序或倒叙)的数字按钮,其中按钮是使用什么规则去排序的,属于可变的部分,而创建按钮则是不变的部分,我们可以把可变的部分封装在函数参数中。

例子:

//不变的部分
function each(ary, callback) {
    let frag = document.createdocumentFragment();
    callback(ary).forEach(v => {
 let node = document.createElement("button");
 node.innerHTML = v;
 frag.appendChild(node);
    });
    document.body.appendChild(frag);
}

//变化的部分:从小到大排列
function sortEach(data) {
    let len = data.length;
    while (--len) {
 for (let i = 0; i < len; i++) {
     if (data[i] > data[i + 1]) {
  [data[i], data[i + 1]] = [data[i + 1], data[i]];
     }
 }
    }
    return data;
}

//变化的部分:从大到小排列
function reverseEach(data) {
    let len = data.length;
    while (--len) {
 for (let i = 0; i < len; i++) {
     if (data[i] < data[i + 1]) {
  [data[i], data[i + 1]] = [data[i + 1], data[i]];
     }
 }
    }
    return data;
}

//each([2, 3, 1, 5, 4], sortEach);
each([2, 3, 1, 5, 4], reverseEach);
//输出: 5 4 3 2 1
回调地狱

首先,考虑下面这个经典的“回调地狱”代码:

addEventListener("click", function handler() {
    setTimeout(function request() {
 ajax("http://some.url", function response(data) {
     if (data == "hello") {
  handler();
     } else if (data == "world") {
  request();
     }
 });
    }, 500);
});

一眼看去,我们似乎可以很快梳理出这样的异步执行顺序:等待 click 事件,然后等待定时器启动,然后等待 Ajax 响应返回。但是,这样的执行顺序,只有在最理想的状态下才会发生,并没有考虑可能导致执行顺序偏离的异常情况,例如,“定时器启动”这一步骤执行失败,那么就永远不会到达“ Ajax 响应”这一步,我们不得不转到“定时器启动”失败时的错误处理流程中。这只是我们列举的一种情况,而实际开发中,我们需要为每一个异步事件指定所有的可能性和路径,那么代码就会变得非常复杂,追踪异步流的难度会成倍增加。这就是回调地狱。
从根本上讲,回调地狱的出现,是因为我们丧失了自己程序一部分的控制权,这种“控制转移”导致我们不得不“核实”任何可能出现的情况。


如有错误,欢迎指正,本人不胜感激。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/245236.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号