原稿:语雀 · Promise 相关题目 · 原目录:面向面经学习 › Promise 相关题目

笔记正文

希望 1000ms 后输出 “0,1,2,3,4”

  • 下列代码的输出,解释

  • 如何修改,以实现目标

for (var a = 0; a < 5; a++) {
  setTimeout(() => console.log(a), 1000);
}

上述代码打印出五个5,修改方案

// 方案一:保存参数
for (var a = 0; a < 5; a++) {
  setTimeout((a) => console.log(a), 1000*a, a);
}
// 方案二:使用let
for (let a = 0; a < 5; a++) {
  setTimeout(() => console.log(a), 1000*a);
}
// 方案三:使用promise
for (var a = 0; a < 5; a++) {
  Promise.resolve(a).then((value) => {
    setTimeout(() => {
      console.log(value);
    }, 1000 * value);
  });
}
// 方案四:使用立即函数:使用IFEE函数体充当块级作用域,内部匿名函数成为IFEE闭包
for (var a = 0; a < 5; a++) {
  (function (i) {
    setTimeout(() => console.log(i), 1000 * i);
  })(a);
}

看代码说输出,理由

console.log(1);
setTimeout(() => {
  console.log(2);
});
new Promise((resolve) => {
  console.log(3);
  resolve("resolve");
  console.log(4);
  reject("error");
})
  .catch((err) => {
    console.log(err);
  })
  .then((res) => {
    console.log(res);
  });
Promise.resolve().then(() => {
  console.log(5);
});
console.log(6);

输出顺序:1 3 4 6 5 resolve 2

1 3 4 6易得,主线程空闲后,首先检查微任务队列,依次为:(11行) catch()回调 <– (17行) then() 回调,放入栈中执行,因为第一个 promise转换为 resolved,所以 catch() 回调不会执行,它会把上一个 value 传递给下面的 then(),后者的回调立刻进入微任务队列,队列变成 :(17行) then()回调 <– (14行) then()回调,接下来才会输出 5 “resolve”,最后执行一个宏任务,输出2


console.log(1);
setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});
new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})
setTimeout(() => {
  console.log(6);
})
console.log(7);

输出顺序:1 4 7 5 2 3 6


async function async1() {
  console.log('async1 start');//2
  await async2();
  console.log('async1 end');// 6
}
async function async2() {
  console.log('async2');//3
}

console.log('script start');//1
setTimeout(function() {
  console.log('setTimeout');//8
}, 0)
async1();
new Promise(function(resolve) {
  console.log('promise1');//4
  resolve();
}).then(function() {
  console.log('promise2');//7
});
console.log('script end');//5

1)代码补充 asyncGetValue(),使之实现 asyncGetValue(1,2).then(v => console.log(v));

function asyncAdd(a, b, callback) {
doAsyncWork(a, b).then(value => callback(value));
}
function asyncGetValue(a, b) {
.....
}

2)如果是实现 asyncGetValues(list),list是一个对象数组,类似于

const list = [
{a: 1, b: 2},
{a: 100, b: 200},
{a: 11, b: 21},
{a: 31, b: 41},
{a: 41, b: 51}];
function asyncGetValues(list) {
.....
}

3)在第三问的基础上实现并发控制,即有一个最大的并发数 maxConcurreent

解答:可以补全 asyncAdd,变成可执行的函数,方便验证

function asyncAdd(a, b, callback) {
  Promise.resolve(a + b).then((value) => callback(value));
  // doAsyncWork(a, b).then((value) => callback(value));
}

第一问:较简单,注意因为 asyncAdd 没有返回 promise,这里必须进行包装,向 asyncAdd 传入包装后的 resolve

function asyncGetValue(a, b) {
  return new Promise((resolve) => asyncAdd(a, b, resolve));
}

第二问:注意使用 Promise.all

function asyncGetValues(list) {
  let promises = [];
  for (let item of list) {
    promises.push(new Promise((resolve) => asyncAdd(item.a, item.b, resolve)));
  }
  return Promise.all(promises);
}

第三问:注意给 add() 传入的 caller 不能直接是 asyncAdd(item.a, item.b)

function asyncGetValues(list, max) {
  let _max = max;
  let _count = 0;
  let _taskQueue = [];

  function add(caller) {
    return new Promise((resolve) => {
      let task = createTask(caller, resolve);
      if (_count < _max) task();
      else _taskQueue.push(task);
    });
  }
  function createTask(caller, resolve) {
    return () => {
      caller()
        .then(resolve)
        .finally(() => {
          if (_taskQueue.length) {
            let task = _taskQueue.shift();
            task();
            _count--;
          }
        });
      _count++;
    };
  }

  let promises = [];
  for (let item of list) {
    let promise = add(
      () => new Promise((resolve) => asyncAdd(item.a, item.b, resolve))
    );
    promises.push(promise);
  }
  return Promise.all(promises);
}