关于前端:Promise面试题思考延伸

8次阅读

共计 3466 个字符,预计需要花费 9 分钟才能阅读完成。

最近想起之前在 V2EX 上看到的一个问题:一个 async function 数组,怎么一个一个程序执行?

想做到的是,每过一秒,别离打印:
this is 0
this is 1
this 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% 应聘者不及格系列):从 闭包说起

请问以下代码打印出什么数据

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

依据网友总结:

  1. 对于 promise 对象,await 会阻塞函数执行,期待 promise 的 resolve 返回值,作为 await 的后果,而后再执行下一个表达式
  2. 对于非 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 函数的语法糖,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)
})
正文完
 0