一道js执行异步面试问题

gipal
  • 159

写一个函数,对接口 example.com 发出 n 次请求,要求除了第一次请求,往后每次请求都等待上次请求完成并等待50毫秒再进行请求,所有请求结束后,打印出结果。

已对请求进行封装如下:

const getData = function (url, callback) {
    setTimeout(() => {
        if ("function" === typeof callback) {
            callback(+new Date())
        }
    }, +(Math.random() * 1000).toFixed())
}

请问大佬这个怎么解?

回复
阅读 1.5k
4 个回答

getData重新封装, 逻辑比较简洁


// 使用异步函数, 这个是考验js语法熟悉程度
// 重新封装请求函数
const getDataAsync = (url) => new Promise((resolve) => getData(url, resolve));

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const getDataLoop = async function getDataLoop(url, num) {
    for(let i = 0; i < num; i ++){
        const ret = await getDataAsync(url);
        await sleep(50);
        console.log(ret);
    }
}
const getData = function (url, callback) {
                setTimeout(() => {
                    if ("function" === typeof callback) {
                        callback(+new Date())
                    }
                }, +(Math.random() * 1000).toFixed())
            }

            function callback(time) {
                console.log(time);
                setTimeout(getData, 50, 'example.com', callback);
            }

            getData('example.com', callback);
function asyncList(count){
    count = count || 0
    let p = Promise.resolve()
    for(let i = 0; i < count; i++){
        p = p.then(_ => {
            return new Promise((resolve) => {
                getData('example.com', function(t){
                    //其他逻辑代码
                    //...
                    resolve(t)
                })
            })
        })
    }
    return p
}
// 发送10个请求
asyncList(10)
边城
  • 54.8k

如果用 Callback 的方式,其实可以理解为递归

const getData = function (url, callback) {
    setTimeout(() => {
        if ("function" === typeof callback) {
            callback(+new Date())
        }
    }, +(Math.random() * 1000).toFixed())
}

// 定义一个函数来完成 n 次连续请求
function requireMoreTimes(url, n, callback) {
    // 每次请求的结果都添加到 result 中 
    const results = [];

    // 定义一个递归函数,请求一次
    // 参数 n 用于传递剩余请求次数
    const requireOne = (n) => {
        // 当 n === 0 的时候,说明完成了所有请求
        // 调用 callback 返回结果,结束递归
        if (n === 0) {
            callback(results);
            return;
        }

        // 每次请求都是调用的 getData 并等待结果
        getData(url, data => {
            // 有结果之后会调用此回调
            // 所以先保存结果
            results.push(data);
            // 再调用下一次 requireOne,注意 n 计数 -1
            setTimeout(() => requireOne(n - 1), 50);
        })
    }

    requireOne(n);
}

requireMoreTimes("", 5, data => console.log(data));

// 结果:
// [
//     1624808425346,
//     1624808425566,
//     1624808425832,
//     1624808426099,
//     1624808426633
// ]

上面这种方式完全是使用的递归的思想(没办法用简单的循环来处理连续异步调用)。但是如果要用循环来实现连续异步,也不是不可以,封装 Promise 再使用 await 就行

// 封装 Promise 版 getData
const asyncGetData = url => {
    return new Promise(resolve => getData(url, resolve));
}

// 还要等 50ms,再封个 wait
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

// 然后连接请求 n 次
async function requireMoreTimes(url, n) {
    const result = [];
    // 第 1 次不需要等 50ms,所以单独写
    // 注1:单独写最后一次也行,道理是一样的
    // 注2:不单独写也行,只是最后一次要等 50ms 才会有结果,这 50ms 是不必要的
    result.push(await asyncGetData(url));
    if (n === 1) {
        return result;
    }

    // 接下来的都要等
    for (let i = 1; i < n; i++) {
        await wait(50);
        result.push(await asyncGetData(url));
    }

    return result;
}

// await 需要在 async 函数中使用,所以封一个 async 的 IIFE
// 当然也可以用  requireMoreTimes("", 5).then(result => console.log(result));
// 不过既然用了 await,就保持风格一致吧
(async () => {
    const result = await requireMoreTimes("", 5);
    console.log(result);
})();
宣传栏