幸运的兔脚

2018年12月20日

阿里前端测试题:实现mergePromise函数

原题

实现 mergePromise 函数,把传进去的数组顺序先后执行,并且把返回的数据先后放到数组 data 中。

const timeout = ms =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, ms)
  })

const ajax1 = () =>
  timeout(2000).then(() => {
    console.log('1')
    return 1
  })

const ajax2 = () =>
  timeout(1000).then(() => {
    console.log('2')
    return 2
  })

const ajax3 = () =>
  timeout(2000).then(() => {
    console.log('3')
    return 3
  })

const mergePromise = ajaxArray => {
  // 在这里实现你的代码
}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
  console.log('done')
  console.log(data) // data 为 [1, 2, 3]
})

// 分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]

解题思路

这题主要是希望使用同步的方法线性的执行函数ajax1, ajax2, ajax3,在本题中,函数ajax是一个定时器,在设制定时器后执行异步打印,因为ajax2设置的时间小于ajax1ajax3,所以如果线性调用的话,正常来说ajax2会早于ajax1ajax3返回,所以在做这一题时的核心思路就是只有当前函数完全执行完毕后,才会进行下一个函数的执行

我的答案

const mergePromise = ajaxArray => {
  // 在这里实现你的代码
  return new Promise(async function(resolve) {
    let data = []
    for (let it of ajaxArray) {
      let tmp = await it()
      data.push(tmp)
    }
    resolve(data)
  })
}

利用async函数只有在前一个await返回后才会才会进入下一个await的特点,实现线性执行异步函数。如此在循环中每次遇到await时,都会等待ajax函数返回才会继续后面的语句,这样就可以用同步的方式执行完所有的异步函数了。

网上的其他答案

const mergePromise = ajaxArray => {
  // 在这里实现你的代码
  var data = []
  var sequence = Promise.resolve()
  ajaxArray.forEach(function(item) {
    sequence = sequence.then(item).then(function(res) {
      data.push(res)
      return data
    })
  })

  return sequence
}

怎么说呢,这个答案就比较高深了,第一眼完全没有看懂,仔细分析之后,才大致有了个概念。

首先,因为mergePromise只是一个普通函数,不可能会有.then方法,所以他在mergePromise函数中,返回了sequence对象,这个对象返回的是Promise.resolve(),从而达到后续代码可以正常执行的目的。

第二,通过forEach方法,执行了数组中的每个函数,根据Promise的特性,在前一个then未执行完时,是不会进行后面then的执行的,所以他在sequence.then(item).then(function(res){...})这个流程中,线性的执行完了每一个ajax函数。

第三,根据Promise的特性,每一个then的返回值都是一个Promise对象,所以即使在前面只是执行了Promise.resolve(),他也可以在后面通过return datadata传递给Promise对象,保证了后面执行mergePromise([ajax1, ajax2, ajax3]).then(data => {...})时,then可以正常接收到传参。

答案来源