看源码学习的技巧

  1. 递归调用同一个函数,在异步代码里也可以执行

    (来自fake-webpack对loader的处理)

    参考链接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    function execLoaders(request, loaders, content, options) {
    return new Promise((resolve, reject) => {
    if (!loaders.length) {
    resolve(content);
    return;
    }

    let loaderFunctions = [];
    loaders.forEach(loaderName => {
    let loader = require(loaderName);
    loaderFunctions.push(loader);
    });

    nextLoader(content);

    /***
    * 调用下一个 loader
    * @param {string} content 上一个loader的输出字符串
    */
    function nextLoader(content) {
    if (!loaderFunctions.length) {
    resolve(content);
    return;
    }
    // 请注意: loader有同步和异步两种类型。对于异步loader,如 less-loader,
    // 需要执行 async() 和 callback(),以修改标志位和回传字符串
    let async = false;
    let context = {
    request,
    async: () => {
    async = true;
    },
    callback: (content) => {
    nextLoader(content);
    }
    };

    let ret = loaderFunctions.pop().call(context, content);
    if(!async) {
    // 递归调用下一个 loader
    nextLoader(ret);
    }

    }
    });

    }

    用一个数组存放所有要执行的函数,在nextLoader函数里调用每一个数组里的函数。

    递归调用nextLoader 函数时,每次将数组pop出来一个区执行。

    当数组里某一个函数是异步函数时,设置一个标志位async,这样在nextLoader函数里面就知道里面的函数(也就是数组的某一个元素,假如是a)是异步的,这时暂停递归。当a异步结果回来的之后,再调用callback,调用callback也就是调用nextLoader,此时之前暂时停掉的递归链子又串起来了。