一道Promise处理多个异步任务的面试题

11 篇文章 0 订阅
2 篇文章 0 订阅

端点笔试题

感觉这家公司的有几道题质量挺高的,但是有个问题是…有几道题居然重复了,这…

编程题

按照顺序加载多个JS文件如a,b,c,d,并且按照顺序依次插入到页面中,等到所有的JS文件加载完再执行Y。

考察:Promise理解,函数封装,onload事件处理。

(当时憋尿憋坏了…用了一个Promise.all,不过这道题出的挺好的)

当时的思路

  1. 将所有任务包装成promise对象放入数组,然后传入Proimse.All().then;
  2. 每个promise对象将script标签压入数组;
  3. promise.All().then(将script标签插入页面).then(执行y)

模拟当时的代码

let srcCon = ["node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js"];

let scriptCon = [];
let proCon = [];

for (let i = 0; i < srcContainer.length; i++) {
    let script = document.createElement('script');

    let promise = new Promise(resolve => {
        // 1
        script.onload = function () {
            console.log('script' + i + 'loaded');
            resolve();
        }
    }).then(() => {
        // script标签放入数组
        scriptCon.push(script);
    });
    
    // 将promise对象压入数组
    proCon.push(promise);

    script.src = srcContainer[i];
}

Promise.all(proCon).then(() => {
    // 2
    for (let i = 0; i < scriptCon.length; i++) {
        document.body.appendChild(scriptCon[i]);
    }
}).then(() => {
    console.log('y');
})

这里犯了一个很严重的错误,标记1和2的地方。

  1. onload的方法是在页面插入script标签之后才会执行的。

    -> 在new中的回调直接push script标签

    -> 将插入标签的操作提到外面。

    let srcCon = ["node_modules/_vue@2.6.12@vue/dist/vue.js",
                  "node_modules/_vue@2.6.12@vue/dist/vue.js",
                  "node_modules/_vue@2.6.12@vue/dist/vue.js",
                  "node_modules/_vue@2.6.12@vue/dist/vue.js",
                  "node_modules/_vue@2.6.12@vue/dist/vue.js",
                  "node_modules/_vue@2.6.12@vue/dist/vue.js"];
    
    let scriptCon = [];
    let proCon = [];
    
    for (let i = 0; i < srcCon.length; i++) {
        let script = document.createElement('script');
    
        let promise = new Promise(resolve => {
            script.onload = function () {
                console.log('script' + i + 'loaded');
                resolve();
            }
            scriptCon.push(script);
        })
    
        proCon.push(promise);
    
        script.src = srcCon[i];
    }
    
    
    for (let i = 0; i < scriptCon.length; i++) {
        document.body.appendChild(scriptCon[i]);
    }
    
    Promise.all(proCon).then(() => {
        console.log('y');
    });
    
  2. 第二点也是非常非常重要的一点,Promise.all是并行执行各个异步事件的,也就是说,不能保证执行的先后顺序

在这里插入图片描述

  1. 我们在resolve中传入每次参与遍历的变量的i

    Promise.all(proCon).then(num => {
        console.log(num);
    });
    //[0, 1, 2, 3, 4, 5]
    

    可以看到,promise会为我们返回一个数组,这个数组的结果是按照all中的数组放入的。

小结

  1. load事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。
  2. Promise.all会并行执行多个异步任务,但是不能保证执行顺序,返回的结果是按照顺序的。

反思

  • 其实将script标签放入数组统一插入body并不会节省性能,因为根本不会触发重绘和回流。
  • 因此应该有多少个src就创建多少个任务,等到上一个任务结束再执行下一个
let srcCon = ["node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js",
              "node_modules/_vue@2.6.12@vue/dist/vue.js"];

let promise = Promise.resolve();

promise.then(() => {
    return new Promise(resolve => {
        let script = document.createElement('script');
        script.onload = function () {
            console.log(1);
            resolve();
        }
        script.src = srcCon[0];
        document.body.appendChild(script);
    })
}).then(() => {
    return new Promise(resolve => {
        let script = document.createElement('script');
        script.onload = function () {
            console.log(2);
            resolve();
        }
        script.src = srcCon[0];
        document.body.appendChild(script);
    })
}).then(() => {
    return new Promise(resolve => {
        let script = document.createElement('script');
        script.onload = function () {
            console.log(3);
            resolve();
        }
        script.src = srcCon[0];
        document.body.appendChild(script);
    })
}).then(() => {
    return new Promise(resolve => {
        let script = document.createElement('script');
        script.onload = function () {
            console.log(4);
            resolve();
        }
        script.src = srcCon[0];
        document.body.appendChild(script);
    })
})

思路:每次都生成一个新的Promise对象管控下一个then中的回调,只有上一个script执行了load,加载完成之后才执行下一个任务。

在这里插入图片描述

当然了,这么冗余的代码肯定是不行的。

let promise = Promise.resolve();

function insertScript(src, i) {
    return new Promise(resolve => {
        let script = document.createElement('script');
        script.onload = function () {
            console.log(i);
            resolve();
        }
        script.src = srcCon[i];
        document.body.appendChild(script);
    })
}

for (let i = 0; i < srcCon.length; i++) {
    promise = promise.then(() => {
        return insertScript(srcCon[i], i);
    });
}

promise.then(() => {
    console.log('y');
})

在这里插入图片描述

吃饭!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值