Promise面试题思考延伸


最近想起之前在V2EX上看到的一个问题:一个 async function 数组, 怎样一个一个顺序执行?[1]
想做到的是,每过一秒,分别打印:this is 0this is 1this is 2~this is 8
下面的代码结果是过一秒后全部执行。
是不是哪里写的不对呢,多谢指教
var jobs = [];for(let i = 0; i < 8; i++) { jobs.push(async function(){     setTimeout(function(){         console.log("this is " + i)     },1000) });}(async function () { for (const job of jobs) {     await job() }})();
这题考察对 Promise 、async/await 的理解。而这题又能让人联想到一个类似的setTimeout 循环问题——破解前端面试(80% 应聘者不及格系列):从 闭包说起[2]
请问以下代码打印出什么数据
for (var i = 0; i < 5; i++) { setTimeout(function() {     console.log(new Date, i); }, 1000);}console.log(new Date, i);
如何修改成每隔1秒打印一个数,以0、1、2、3、4、5 的顺序排列,并要求原有的代码块中的循环和两处 console.log 不变,该怎么改造代码?
此题面试官想考察循环、闭包、闭包的解决(IIFE)、ES6 知识点(let、Promise)、ES7 的Async/await,以及setTimeout  的第三个参数等等知识点,具体看文章能明白一二
这里我们讲第一道题目,如果要实现题主所说的效果,缺少了什么?
已知:let形成块级作用域,意味着每次循环,jobs就 push 一个 async 函数,这些都是同步执行
但是注意,async 中的函数也是同步执行,只有等到 await 时才会进入微任务中,所以当
i=0时,jobs 塞入一个 setTimeout(function() { console.log("this is 0" )})
i=1时,jobs 塞入一个 setTimeout(function() { console.log("this is 1" )})
i=2时,jobs 塞入一个 setTimeout(function() { console.log("this is 2" )})
i=3时,jobs 塞入一个 setTimeout(function() { console.log("this is 3" )})
...
i=7时,jobs 塞入一个 setTimeout(function() { console.log("this is 7" )})
继续往下执行
(async function () {    for (const job of jobs) {        await job()    }})();
这里题主了解到 await 需要 async 配合使用,就写了立即执行匿名函数,执行数组 jobs,但问题是 jobs 中的每个子项都是执行 async function(){setTimeout},这里的 async 有意义吗?
jobs.push(async function(){    setTimeout(function(){        console.log("this is " + i)    },1000)});
如果要让 await 暂停进程并恢复进程(即await job()),我们需要的是什么?
去掉 async,使其变成一个普通的函数,结果执行结果一致
jobs.push(function () {    setTimeout(function () {        console.log("this is " + i)    }, 1000)});
同样,将普通函数改成箭头函数也是如此,一秒之后打印还是0~7
根据网友[3]总结:
对于 promise 对象,await 会阻塞函数执行,等待 promise 的 resolve 返回值,作为 await 的结果,然后再执行下一个表达式
对于非 promise 对象,比如 箭头函数、同步表达式等等,await 等待函数或者直接量的返回,而不是等待其执行结果
所以如果要让 await 每隔1秒执行一个 job,那就需返回一个 promise 实例,基于此逻辑进行改造
...jobs.push(function () {    return new Promise((resolve, reject) => {        setTimeout(function () {            resolve()         console.log("this is " + i)     }, 1000)      })});...
这样,就解决了这个问题
我们的逻辑是在循环中,每次向 jobs 中塞入一个函数,这个函数返回的是一个实例 Promise(即 await 遇到后会暂停等异步结束再继续后续代码)。当执行 await job() 时,我们知道是循环 jobs,await 让其等待执行,执行完第一个后,再执行第二个,循序执行,每一个等待1秒钟,就达到题目的要求
这里我们了解到 await 等待的是一个 promise 实例(如果非 promise 实例,就不用等待),既然说到 Promise,我们就延伸一下,then 的链式调用
Promise 的 then 方法支持链式调用,它有哪几种情况?
不 return(返回)值, 值延续上一个 resolved
return
return 非 Promise 实例
return Promise 实例
不 return
const promise = new Promise((resolve, reject) => {  setTimeout(() => {      resolve('johan')  }, 2000)})promise.then((data) => {    console.log(data)    // 不返回任何值}).then((data) => {    console.log('第二次', data)})
答案:johan、第二次 undefined
return 非 Promise 实例
const promise = new Promise((resolve, reject) => {  setTimeout(() => {      resolve('johan')  }, 2000)})promise.then((data) => {    console.log(data)    return '帝王johan'}).then((data) => {    console.log('第二次', data)})
答案:johan、第二次 帝王johan
return Promise 实例
const promise = new Promise((resolve, reject) => {  setTimeout(() => {      resolve('johan')  }, 2000)})promise.then((data) => {    console.log(data)    return new Promise((resolve, reject) => {        setTimeout(() => {            resolve(`${data} next`)        }, 4000)    })}).then((data) => {    console.log('第二次', data)})
答案:johan、第二次 johan next
以上三个例子可以得知,在 then 方法中的  onfulfilled 函数和 onrejected 函数,不仅支持不返回,而且支持非 Promise 实例的普通值,而且支持一个 Promise 实例。并且返回的这个 Promise 实例或非 Promise 实例的普通值将会传给下一个 then 方法的 onfulfilled 函数或者 onrejected 函数中
因为我们知道它是 Generator 函数的语法糖[4],async 函数返回的是一个 Promise 对象,当函数执行时,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
我们来一道题来测试一下
const myPromise = val => Promise.resolve(val);const delay = duration => { /**/ };myPromise(`hello`).then(delay(1000)).then(val => console.log(val)); // hello
myPromise 是个 Promise 实例,传入值 hello,经过 Promise.resolve 传到then 中,然后经 delay 再传递给下一个 then,打印出val,所以 delay(1000) 会返回一个 Promise 实例,这样,第二个 then 才能打印出 hello
const delay = duration => (val) => new Promise((resolve, reject) => {    setTimeout(() => {        resolve(val)    }, duration)})
参考资料
[1] 一个 async function 数组, 怎样一个一个顺序执行?: https://www.v2ex.com/t/810025[2] 破解前端面试(80% 应聘者不及格系列):从 闭包说起: https://zhuanlan.zhihu.com/p/25855075[3] 网友: https://blog.csdn.net/qq_38990451/article/details/114869256[4] Generator 函数的语法糖: https://es6.ruanyifeng.com/?search=async&x=0&y=0#docs/async
⚠️「随朱波流」只是记录本人关于编程、生活、个人成长、思考的自留地。如有需要,可前往 blog.azhubaby.com(阅读原文)中查看第一手文章
到顶部