Daily-Interview-Question
Daily-Interview-Question copied to clipboard
第 160 题:输出以下代码运行结果,为什么?如果希望每隔 1s 输出一个结果,应该如何改造?注意不可改动 square 方法
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
list.forEach(async x=> {
const res = await square(x)
console.log(res)
})
}
test()
forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、4、9。
串行解决方案:
async function test() {
for (let i = 0; i < list.length; i++) {
let x = list[i]
const res = await square(x)
console.log(res)
}
}
当然,也可以用 for of
语法,就是帅:
async function test() {
for (let x of list) {
const res = await square(x)
console.log(res)
}
}
还有一个更硬核点的,也是 axios 源码里所用到的,利用 promise 本身的链式调用来实现串行。
let promise = Promise.resolve()
function test(i = 0) {
if (i === list.length) return
promise = promise.then(() => square(list[i]))
test(i + 1)
}
test()
一秒后同时输出 1、4、9
如果要每隔一秒输出把 forEach 换成普通 for 循环或者 for...of... 循环即可
这里并行进行是因为 forEach 实现的问题,源码里用 while 来一次性执行了所有回调
具体参考官网 polyfill: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、2、3。
串行解决方案:
async function test() { for (let i = 0; i < list.length; i++) { let x = list[i] const res = await square(x) console.log(res) } }
同时输出的是 1、2、3 的平方也就是 1 、4、9
async function test() {
var n = list.length;
while(n--) {
const res = await s(list[n]);
console.log(res);
}
}
每隔一秒输出 9 4 1
一秒后同时输出 1、4、9
如果要每隔一秒输出把 forEach 换成普通 for 循环或者 for...of... 循环即可
这里并行进行是因为 forEach 实现的问题,源码里用 while 来一次性执行了所有回调
具体参考官网 polyfill: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
你这个用 while 来一次性执行了所有回调描述不太准确吧,普通的for 等于同一个块作用域连续await,而forEach的回调是一个个单独的函数,跟其他回调同时执行,互不干扰
function test() {
list.forEach(async x=> {
const res = await square(x)
console.log(res)
})
//forEach循环等于三个匿名函数;
(async (x) => {
const res = await square(x)
console.log(res)
})(1);
(async (x) => {
const res = await square(x)
console.log(res)
})(2);
(async (x) => {
const res = await square(x)
console.log(res)
})(3);
// 上面的任务是同时进行
}
async function test() {
for (let x of list) {
const res = await square(x)
console.log(res)
}
}
//等价于
async function test() {
const res = await square(1)
console.log(res)
const res2 = await square(2)
console.log(res)
const res3 = await square(3)
console.log(res)
}
@sl1673495 窃以为您的第三个串行方案的第四行代码后面应该加上.then(res => console.log(res))
《ES6 入门》中还提到了使用 reduce 解决:
list.reduce(async (_, x) => {
await _
const res = await square(x)
console.log(res)
}, undefined)
直接在forEach 里面套个setTimeOut 不就可以了
function test() { list.forEach(async x => { setTimeout(async () => { const res = await square(x) console.log(res) }, 1000 * x) }) }
《ES6 入门》中还提到了使用 reduce 解决:
list.reduce(async (_, x) => { await _ const res = await square(x) console.log(res) }, undefined)
await _ 这个怎么理解呀 大哥
《ES6 入门》里面是这样介绍的:
reduce 方法的第一个参数是 async 函数,导致该函数的第一个参数是前一步操作返回的 Promise 对象,所以必须使用await等待它操作结束
如果没有 await _
这一行,得到的结果和 forEach()
是一样的,可以理解为 @linsicong003 所说的:源码里用 while 来一次性执行了所有回调
?
添加一行打印:
function test() {
list.reduce(async (_, x) => {
console.log(_, x) // 打印
await _
const res = await square(x)
console.log(res)
}, undefined)
}
test()
打印结果:
马上打印:
undefined 1
Promise { <pending> } 2
Promise { <pending> } 3
每隔一秒打印:
1
4
9
为什么大厂面试的题都这么蹊跷???
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
;(async () => {
for await (let num of list) {
const res = await square(num)
console.log(res)
}
})()
}
test()
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
list.forEach(x=> {
setTimeout(async x => {
const res = await square(x)
console.log(res)
}, x*1000, x)
})
}
test()
function test() {
list.reduce(
(pre, cur) => pre.then(() => square(cur)).then(console.log),
Promise.resolve()
);
}
test();
// 解法一,for循环
async function test() {
for (let i = 0, len = list.length; i < len; i++) {
const res = await square(list[i])
console.log(res)
}
}
// 解法2 for of循环
async function test() {
for (x of list) {
const res = await square(x)
console.log(res)
}
}
// 解法3 类似koa里面的compose思想解决
async function test() {
const compose = (middleware, next) => {
let index = -1
return (ctx) => {
const dispatch = (i) => {
if (index < i ) {
index = i
}
let fn = middleware[ index ]
if (index === middleware.length) {
fn = next
}
if (!fn) {
return Promise.resolve()
}
return Promise.resolve(fn(ctx, async () => {
return dispatch(index + 1)
}))
}
dispatch(0)
}
}
const middleware = []
list.forEach((it) => {
middleware.push(async (ctx, next) => {
const res = await square(it)
console.log(res)
await next()
})
})
compose(middleware, () => {
console.log('end')
})({ name: 'qianlongo' })
}
// 解法4 next思想 也是koa1的中间件执行思想
async function test() {
let middleware = []
list.forEach((it) =>{
middleware.push(async (cb) => {
const res = await square(it)
console.log(res)
cb && cb()
})
})
const bindNext = (cbs) => {
let next = function () {
console.log('111')
}
let len = cbs.length
while (len--) {
next = cbs[ len ].bind(null, next)
}
return next
}
bindNext(middleware)()
}
// 解法5 利用promise的链式调用
function test() {
let promise = Promise.resolve()
list.forEach(x => {
promise = promise.then(() => square(x)).then((res) => {
console.log(res)
})
})
}
test()
题目本身有些问题,定时功能实现 square 中需要进行误差修正的
forEach的回调是一个个单独的函数,跟其他回调同时执行
精品
《ES6 入门》中还提到了使用 reduce 解决:
list.reduce(async (_, x) => { await _ const res = await square(x) console.log(res) }, undefined)
大哥 这里面都没有return 为什么await _ (_) 会是上一次的方法呢 怎么累积的呢 ??? 传统的 不都是这样的吗 var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); 能帮忙解答下吗 迷茫。。。。。
forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、4、9。
串行解决方案:
async function test() { for (let i = 0; i < list.length; i++) { let x = list[i] const res = await square(x) console.log(res) } }
当然,也可以用
for of
语法,就是帅:async function test() { for (let x of list) { const res = await square(x) console.log(res) } }
还有一个更硬核点的,也是 axios 源码里所用到的,利用 promise 本身的链式调用来实现串行。
let promise = Promise.resolve() function test(i = 0) { if (i === list.length) return promise = promise.then(() => square(list[i])) test(i + 1) } test()
promise的实现根本没有输出呢
《ES6 入门》中还提到了使用 reduce 解决:
list.reduce(async (_, x) => { await _ const res = await square(x) console.log(res) }, undefined)
大哥 这里面都没有return 为什么await _ (_) 会是上一次的方法呢 怎么累积的呢 ??? 传统的 不都是这样的吗 var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); 能帮忙解答下吗 迷茫。。。。。
因为async函数的返回值是 Promise 对象
const asyncFunc = async () => {}
console.log(asyncFunc()) // Promise {<resolved>: undefined}
一道题可以导出好多知识点,受教了各位大佬
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
//迭代器实现
async function test () {
var iter = list[Symbol.iterator]();
var flag = iter.next();
while (!flag.done) {
await square(flag.value).then(res => console.log(res));
flag = iter.next();
}
}
test();
forEach大概可以这么理解
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}
所以是1秒后输出1, 4, 9; 几乎同时;但是有调用先后顺序; 改进的话, 大家都答了, 我就不嫌丑了...
我以为我会,我答出来了,过来一看,我还是太年轻了😅你们都是大佬
const list = [1, 2, 3];
const square = num => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
async function test() {
for (let x of list) {
const res = await square(x)
console.log(res)
}
}
test()
《ES6 入门》中还提到了使用 reduce 解决:
list.reduce(async (_, x) => { await _ const res = await square(x) console.log(res) }, undefined)
大哥 这里面都没有return 为什么await _ (_) 会是上一次的方法呢 怎么累积的呢 ??? 传统的 不都是这样的吗 var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); 能帮忙解答下吗 迷茫。。。。。
console.log((async ()=>{})()) // 函数执行返回的是一个 Promise
function test() {
async function next(list,index=0){
if(index===list.length)return;
const res = await square(list[index])
next(list,index+1)
}
next(list)
}
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
list.forEach(async x=> {
const res = await square(x)
setTimeout(() => console.log(res), (x - 1) * 1000)
})
}
test()
打印结果是一秒后直接打印 1 4 9 原因:forEach方式是不能阻塞的,在一瞬间执行完之后会同时产生3个SetTimeout任务,然后再一秒后同时打印
解决思路:将结果保存在Promise中,用then的方式依次调用
function test() {
let p = Promise.resolve();
list.forEach((index) => {
p = p.then(res => {
return square(index).then(res => {
console.log(res)
})
})
});
}
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
await Promises.all(list.forEach(async x=> { /* Promises.all takes all promises and return them when they all resolve */
const res = await square(x)
console.log(res)
}));
}
test()`
forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、4、9。
串行解决方案:
async function test() { for (let i = 0; i < list.length; i++) { let x = list[i] const res = await square(x) console.log(res) } }
当然,也可以用
for of
语法,就是帅:async function test() { for (let x of list) { const res = await square(x) console.log(res) } }
还有一个更硬核点的,也是 axios 源码里所用到的,利用 promise 本身的链式调用来实现串行。
let promise = Promise.resolve() function test(i = 0) { if (i === list.length) return promise = promise.then(() => square(list[i])) test(i + 1) } test()
第二个方法忘记输出了 因为 square
返回一个 promise
所以可以这样输出:
promise = promise.then(() => square(list[i]))
// 修改为
promise = promise.then(() => square(list[i]).then(num => console.log(num)))
Promise 链
function test() {
let promise = Promise.resolve();
const start = Date.now();
for (let i = 0; i < list.length; i++) {
promise = promise.then(() => {
const res = square(list[i]);
return res;
}).then((value) => {
console.log(`${Date.now() - start}`, value)
});
}
}
function test() {
const newlist = list.map(item => square(item))
let index = arguments[0] || 0
if (index === newlist.length) {
return
}
newlist[index].then(res => {
console.log(res)
test(index+=1)
})
}
// for of
async function test() {
for(let x of list){
const res = await square(x)
console.log(res)
}
}
// reduce + promise
function test(){
list.reduce((p, v) => {
return p.then(()=> square(v).then(console.log))
}, Promise.resolve())
}
// 和上面基本一致
function test(){
let p = Promise.resolve()
let i=0
while(list[i]!==undefined){
let v = list[i++]
p = p.then(()=>square(v).then(console.log))
}
}
if (i === list.length) return
第三种方法中,if (i === list.length) return 条件应该是 list.length - 1 吧 因为你是从0开始的
// while 循环
async function test1() {
while (list.length) {
const num = list.shift();
const res = await square(num);
console.log(res);
}
}
// 传统的for
async function test2() {
for (const item of list) {
const res = await square(item);
console.log(res);
}
}
// promise 队列
async function test3() {
let p = Promise.resolve();
for (let i = 0; i < list.length; i += 1) {
p = p.then(() => square(list[i])).then((res) => console.log(res));
}
}
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
list.reduce((prev,next)=>{
return prev.then(()=> square(next).then(res=>console.log(res)))
},Promise.resolve())
}
test()
const list = [1, 2, 3];
const square = async (num) => { return await new Promise((resolve, reject) => { setTimeout(() => { resolve(num * num); }, 1000); }); };
const test = async () => { for (let x of list) { const res = await square(x); console.log(res); } };
test();
以前面试遇到一个差不多的题,当时用的是链式递归实现的:
function test(i = 0) {
if (i === list.length) return
return square(list[i]).then(res => {
console.log(res);
return test(++i)
})
}
test()
对于forEach的并行处理,可以用下面这段代码来学习一下:
const count = (item) => {
return new Promise((resolve) => {
if (typeof item === 'number') {
setTimeout(() => {
console.log(item);
resolve();
}, 500);
} else {
setTimeout(() => {
console.log(item);
resolve();
}, 1000);
}
})
}
// forEach
let arr = [1, 'a', 'b', 'c', 2];
arr.forEach(async item => {
const ret = await count(item);
});
// 1
// 2
// a
// b
// c
// for
(async function test() {
for (let i = 0; i < arr.length; i++) {
await count(arr[i]);
}
})()
// for ... of
(async function test() {
for (let x of arr) {
const res = await count(x)
}
})()
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test(i=0) {
console.log('i', i)
square(list[i]).then(num=>{
console.log(num)
if(list[i+1]){
test(i+1)
}
})
}
test()
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
(async function test() {
for (let x of list) {
const res = await square(x);
console.log(res);
}
})()
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
test.p = Promise.resolve();
list.forEach(x => {
test.p = test.p.then(() => square(x)).then(console.log)
})
}
test()
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
let promise = Promise.resolve();
list.forEach(x => {
promise = promise.then(() => square(x)).then(res => console.log(res))
})
}
test()
第三种方式是有缺陷的,1秒后同时输出1,4,9
给出改进的方法
const list = [1, 2, 3] const square = num => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num * num) }, 1000) }) } let promise = Promise.resolve(); function test(i = 0) { if (i === list.length) return; promise = promise.then(async () => { await square(list[i]).then(res => { console.log('res', res) }) }); test(i + 1) } test()
直接在forEach 里面套个setTimeOut 不就可以了
function test() { list.forEach(async x => { setTimeout(async () => { const res = await square(x) console.log(res) }, 1000 * x) }) }
这角度相当刁钻了,就是但凡数组不是连续整数这代码还得改。。。应该用index去乘。
function test() {
var p = Promise.resolve();
list.forEach(x => {
p = p.then(() => square(x).then(res => console.log(res)));
});
}
function test() { list.reduce(async (p, x)=> { await p; const res = await square(x) console.log(res) }, Promise.resolve()) }
function test() {
list.reduce(async (p, x)=> {
await p;
const res = await square(x)
console.log(res, +new Date() / 1000)
}, Promise.resolve())
}
`const list = [1, 2, 3] const square = num => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num * num) }, 1000) }) }
function test() { (async()=>{ for (let index = 0; index < 3; index++) { const res = await square(index) console.log(new Date(), res) } })() } test() `
改造test就好了
function test() {
list.forEach(async(x, index)=> {
const res = await square(x)
setTimeout(() => {console.log(res)}, 1000*index)
})
}
用reduce实现链式调用
const list = [1, 2, 3]
const square = num=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(num * num)
}
, 1000)
}
)
}
function test() {
list.reduce((p,x)=>{
return p.then(async()=>{
const res = await square(x)
console.log(res)
}
)
}
, Promise.resolve())
}
test()
Hi~思聪已经收到你的邮件咯~待会就回复哦
重写 forEach
Array.prototype.forEach = async function(callback, thisArg) {
let bindThis, idx;
if (this === null) {
throw new TypeError('this is null or not defined');
}
let arrayObj = Object(this);
let len = O.length >>> 0;
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function')
}
if (arguments.length > 1) {
bindThis = thisArg;
}
idx = 0
while (idx < len) {
let item;
if (idx in arrayObj) {
item = arrayObj[idx];
await callback.call(bindThis, item, idx, arrayObj);
}
idx++;
}
}
arr = new Array(1,2,3);
async function foo(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n + n);
}, 1000);
});
}
arr.forEach(async (item) => {
console.log(await foo(item));
});
Hi~思聪已经收到你的邮件咯~待会就回复哦
const list = [1, 2, 3];
const square = (num) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num);
}, 1000);
});
};
async function test() {
for (const item of list) {
const res = await square(item);
console.log(res);
}
}
test();
for for of
reduce
function test() {
list.reduce((acc,cur,index)=> acc.then(()=>square(list[index])),Promise.resolve());
}
Hi~思聪已经收到你的邮件咯~待会就回复哦
const list = [1, 2, 3];
const square = (num) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num);
}, 1000);
});
};
async function test(index) {
const val = list[index];
if (val === undefined) return;
const res = await square(val);
console.log(res);
test(index + 1);
}
test(0);