1

一道关于Promise应用的面试题

题目:红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promse实现)

为了方便演示控制台里执行代码,增加额外的条件:只需要循环3次。

1. Promise最常规实现

function red () {
    console.log('red')
}

function green () {
    console.log('green')
}

function yellow() {
    console.log('yellow')
}

var tic = function(timmer, cb){
    return new Promise(function(resolve, reject) {
        cb();
        setTimeout(resolve, timmer);
    });
}

var round = 0;
var maxRound = 3;
;(function step() {
    if(++round > maxRound ) {
        return;
    }
    console.log(`Round ${round}`)
    tic(3000, red)
    .then(function() {
        return tic(2000, green)
    })
    .then(function() {
        return tic(1000, yellow)
    })
    .then(step)
})()

2. 使用 Array.prototype.reduce方法优化调用方式

题目本质就是元素前后存在依赖的任务,可以采用Array.prototype.reduce方法优化调用方式。

var round = 0;
var maxRound = 3;
;(function step(){
     if(++round > maxRound) {
        return;
    }
    console.log(`Round ${round}`);
    [red, green, yellow, step].reduce((prev, cb, index, arr) => {
        return prev.then(() => {
            return tic(1000 * (arr.length - index - 1), cb)
        })
    }, Promise.resolve())
})()

3. 基于Generator函数的实现

var round = 0;
var maxRound = 3;

function* gen() {    
    yield tic(3000,red);
    yield tic(2000, green);
    yield tic(1000, yellow)
}
(function step() {
    if(++round > maxRound) {
        return;
    }
    console.log(`Round ${round}`);
    
    var g = gen();
    (function next(){
        var result = g.next();
        if(result.done) {
           step();
        } else {
            result.value.then(next)
        }
    })()    
})();
  1. 果然使用Gengerator函数控制异步流程确实比较烧脑,还是async/await

4. 基于async/await的实现

// 重新定义了tic函数
var tic = function(timmer, cb){
    return () => new Promise(function(resolve, reject) {
            cb();
            setTimeout(resolve, timmer);
        });
}

var round = 0;
var maxRound = 3;

(async function step() { 
   if(++round > maxRound) {
        return;
    }
    console.log(`Round ${round}`);
    ;[tic(3000,red), tic(2000, green), tic(1000, yellow), step]
    .reduce(async (preTask, currentTaskFunc) => {
        await preTask; // 等上一个任务结束,再执行当前任务
        await currentTaskFunc(); // 或则 `return currentTaskFunc()`
    }, void 0)
})()
  1. reduce方法的第二个实参传递是undefined, 再加上第一个实参是个async函数并且返回值也是undefined,所以await preTask的值是undefined

5. 基于callback实现

主要是看评论区里有人提供了callback方案,想着跟Expressjs的中间件函数调用类似,自己也写个callback方式的:

function red (next) {
    console.log('red')
    setTimeout(next, 3000)
}

function green (next) {
    console.log('green')
    setTimeout(next, 2000)
}

function yellow(next) {
    console.log('yellow')
    setTimeout(next, 1000)
}

var round = 0;
var maxRound = 3;
;(function step() {
    if(++round > maxRound ) {
        return;
    }
    console.log(`Round ${round}`)

    var tasks = [red, green, yellow, step]
    ;(function next() {
        var task = tasks.shift();
        task(next);       
    })()
})()

写法2:


var round = 0;
var maxRound = 3;
var tasks = [red, green, yellow, step];

function step() {
    if(++round > maxRound ) {
        return;
    }
    console.log(`Round ${round}`)
    
    var index = 0;
    ;(function next() {
        var currentTask = tasks[index++];
        currentTask(next);       
    })()
}

step();
  1. 利用索引控制轮询的任务;

普拉斯强
2.7k 声望53 粉丝

Coder