js 有没有队列执行这种东西?

我有个js写的函数如下:

function myAlert(str, config) {
    if ($('#alertBox').length) {
        $('#alertBox').remove();
    }
    var defaultConfig = {
        millisecond: 3000
    };
    config = Object.assign(defaultConfig, config);
    str = '<div id="alertBox">' + str + '</div>';
    $('body').append(str);
    $('#alertBox').css({
        'top': parseFloat((($(window).height()) / 2 + window.scrollY) * 9 / 10) + 'px',
        'left': Math.floor(($(window).width() - $('#alertBox').outerWidth(true)) / 2) + 'px',
        'zIndex': typeof config.zIndex == 'undefined' ? '' : config.zIndex
    }).fadeIn('fast').delay(parseInt(config.millisecond)).fadeOut('fast', function () {
        this.remove();
    });
}

这个函数是用来做提醒的,调用一次页面弹出一个提醒框,显示 3 秒然后自动关掉,现在有个问题,如果我有两次调用,并且调用间隔很小的话,那么第一次的弹框内容还没有显示够 3 秒的时间就直接显示了第二次的弹窗内容给取代了,我尝试了很久不知道怎么解决这个问题,希望有个前端大佬帮忙看看,谢谢。

我的期望是即使我调用 n 次,他每次都要显示我定义的那个 millisecond 时间之后关闭,间隔 300 毫秒后,再继续显示下一个,直到 n 次显示完再关闭。

经大佬指点,我自己总结成了一个方法写到了下面,基本达到目的了,再次谢谢各位回答的大佬!

function oppoMsg(str, config) {
    if ($('#alertBox').length) {
        $('#alertBox').remove();
    }
    str = '<div id="alertBox">' + str + '</div>';
    $('body').append(str);
    $('#alertBox').css({
        'top': parseFloat((($(window).height()) / 2 + window.scrollY) * 9 / 10) + 'px',
        'left': Math.floor(($(window).width() - $('#alertBox').outerWidth(true)) / 2) + 'px',
        'zIndex': typeof config.zIndex == 'undefined' ? '' : config.zIndex
    }).fadeIn('fast').delay(parseInt(config.millisecond)).fadeOut('fast', function () {
        this.remove();
    });
}
function queueFn(fn) {
    const queue = [];
    let running = false;

    function next() {
        if (running) return;
        if (!queue.length) return;
        running = true;
        var args = queue.pop(), config = {millisecond: 3000};//这里做了一些改动
        config = Object.assign(config, args[0][1]);
        try {
            fn.call(this, args[0][0], config);
        } catch (err) {
            console.error(err)
        }
        setTimeout(function () {
            running = false;
            setTimeout(function () {
                next();
            }, 200);//这里又加了一个定时器,先消失再弹出来
        }, config.millisecond);
    }

    return function (...args) {
        queue.push([args, this]);
        next();
    }
}

const queuedOppoMsg = queueFn(oppoMsg);

function myAlert(str, type, config, idName, reportError) {
    type = type || 'alert';
    idName = idName || 'showApiAlert';
    reportError = reportError || false;
    if (reportError) {
    }
    switch (type) {
        case 'alert':
            alert(str);
            break;
        case 'msg':
            //TO DO 要解决这个提醒会被覆盖的问题
            queuedOppoMsg(str, config);
            break;
        case 'notice':
            break;
}

测试代码:

for (var i = 0; i < 2; i++) {
    myAlert('test' + i, 'msg');
}
阅读 470
4 个回答
function queueFn(fn){
  const queue = [];
  let running = false;
  function next(){
    if(running) return;
    if(!queue.length) return;
    running = true;
    const [[str, {zIndex = "", millisecond = 3000}], context] = queue.pop();

    try{
      fn.call(this, str, {zIndex, millisecond});
    } catch(err){console.error(err)}
    
    setTimeout(function(){
      running = false;
      next();
    }, millisecond);
  }
  return function(...args){
    queue.push([args, this]);
    next();
  }
}
const queuedMyAlert = queueFn(myAlert);

原来调用 myAlert 的地方,改为调用 queuedAlert 即可。

这个可以自己实现个队列,也不是特别麻烦,不保证高可用性,与性能的情况下,你就弄个秒级的无限定时器,弄个全局数组,就不断读这个队列里面数据就行了。但是在实际业务里,一般都不会这么做,通常是采取两种方案节流与防抖

  • 节流:在一定时间内,多次触发同一个事件,只执行第一次操作,比如浏览器的滚动事件,或者窗口大小改变事件,我们不可能每滚动一点就去处理,因为那样会造成大量计算,导致页面卡顿。我们可以采用节流的方式,比如每隔200ms处理一次
  • 防抖:在一定时间内,多次触发同一个事件,只执行最后一次操作。常见的应用场景是用户输入搜索词进行实时查询,如果我们每输入一个字就去后台请求,那后台的压力会很大,而且大多数请求都是无用的。所以我们可以加一个防抖,只有当用户输入停止了一段时间后,我们再去请求

我们没必要因为这个功能实现队列,成本太高了

你看看这个例子能否满足你

/** promise写定时器 */
function sleep(interval) {
  return new Promise(resolve => setTimeout(resolve, interval))
}

/** 具体执行的函数 */
async function show() {
  console.log('show', Date.now())
  await sleep(3000)
  console.log('hidden', Date.now())
  await sleep(300)
}

/** 任务列表 */
const task = []

/** 点击事件,添加任务 */
async function clickShow() {
  const first = !task.length
  task.push(show)
  if (first) {
    // 首次添加就执行任务,当异步执行完所有任务后清空任务列表
    for (let i = 0; i < task.length; i++) {
      const fn = task[i]
      await fn()
    }
    task.splice(0, task.length)
  }
}

// 同步调用,添加任务
clickShow()
clickShow()
clickShow()
新手上路,请多包涵

可以加个alertCount来表示当前需要执行多少次。
然后使用setInterval来反复执行你的myAlert,参数里设置了3000ms。
需要注意的是,setInterval第一次也会等3000ms,笨点可以自己手动调用第一次。
也可以把setInterval加在myAlert方法的最后,执行完了以后反复调用。
下面的是个示例代码,你可以参考试试。

var alertCount = 0;
var intervalId;
function Alert(){
    alertCount = 5;
    intervalId = setInterval(myAlert, 3000);
}

function myAlert(str, config) {

    if(alertCount <= 0){
        clearInterval(intervalId);
    }

    if ($('#alertBox').length) {
        $('#alertBox').remove();
    }
    var defaultConfig = {
        millisecond: 3000
    };
    config = Object.assign(defaultConfig, config);
    str = '<div id="alertBox">' + str + '</div>';
    console.log("add");
    $('body').append(str);
    $('#alertBox').css({
        'top': parseFloat((($(window).height()) / 2 + window.scrollY) * 9 / 10) + 'px',
        'left': Math.floor(($(window).width() - $('#alertBox').outerWidth(true)) / 2) + 'px',
        'zIndex': typeof config.zIndex == 'undefined' ? '' : config.zIndex
    }).fadeIn('fast').delay(parseInt(config.millisecond)).fadeOut('fast', function () {
        this.remove();
        console.log("remove");
        alertCount -= 1;
    });
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题