因而,JavaScript 中 Promises 的故事情节和 async /await 句法早已以文档和音频方式被掀开谜样盖头,单次比我数得更要多。 不过,做为我在自学 JavaScript 前夕碰到的极具诱惑力的基本概念众所周知,我下定决心写一则该文来如是说我的自学方式。 所以让他们早已开始吧!
“现代”流程或非堵塞表达式
在自学怎样代码的这天,他们一般来说被知会流程是按次序运转的。 考量下列流程:
function main() { console.log(“1”); console.log(“2”); console.log(“3”); } main(); — Output: 1 2 3此种犯罪行为是他们绝大多数人都生活习惯的。 每带队由上而下由上而下继续执行,当初始化两个表达式时,下带队会等候它继续执行顺利完成接着再运转他们。
不过,在 JavaScript 中,他们有一类称作触发器表达式的小东西(为的是更确切确保安全,我将其称作非堵塞表达式)。 那些表达式在被初始化时不能等候下带队顺利完成继续执行。 让他们看一看附注以更快地认知这一点儿:
function main() { console.log(1) setTimeout(function () { console.log(2), 1000 }) console.log(3); } main(); — Output: 1 3 2输出显示 3 打印在 2 之前。这是因为 setTimeout 表达式是非堵塞的,这意味着当初始化它时,流程会立即运转下带队 console.log(‘3’),而无需等候 setTimeout 首先顺利完成。
但非堵塞表达式有什么意义呢?
我保证他们很快就会兑现承诺(没有双关语),但我相信要认知非堵塞表达式,重要的是要了解它的用途。 想象两个从互联网下载多个文件的流程:
function downloadFile(filename) { setTimeout(function () { console.log(filename + ” downloaded”); }, 1000) } function main() { downloadFile(“file1”) downloadFile(“file2”) downloadFile(“file3”) downloadFile(“file4”) } main(); — Output: file1 downloaded file2 downloaded file3 downloaded file4 downloaded — Time taken: 1.057sdownloadFile表达式代表下载过程,耗时1秒。 现在,即使我初始化了 downloadFile 4 次,顺利完成所有 4 次下载所需的总时间是 1.057 秒。 在较高的层面上,您可以想象所有 4 个 downloadFile 表达式“同时”运转。
这就是非堵塞表达式的威力。 如果他们要等候每个下载顺利完成后再初始化下两个下载表达式,则总共花费的时间将是 4 秒。
现在,需要注意的是,在此种情况下他们可以使用非堵塞表达式的原因是因为每个表达式都独立于其他表达式,并且无论流程遵循什么次序,最终结果都是相同的:所有 4 个文件 下载了。
附: 在 Linux / Mac 上,要对流程从早已开始到结束进行计时,您可以在终端中运转 time node index.js
所以什么是承诺(Promise)呢?
简而言之,当他们想要等候非堵塞表达式时,他们会使用 Promise。 假设我有两个类似 Instagram 的应用流程,我想在流程中继续执行下列操作:
登录用户
表达式:
function login() { return new Promise((resolve, reject) => { setTimeout(function () { resolve(“User logged in”) }, 2000) }) } function getUserPosts() { return new Promise((resolve, reject) =>{ setTimeout(function () { resolve(“Got users posts”) }, 1000) }) }登录是两个需要 2 秒的功能,而获取用户的帖子是两个需要 1 秒的功能。 本质上,通过在每个表达式中返回两个新的 Promise,他们可以说表达式末尾有两个结果,因而即使该表达式是非堵塞的,如果需要,也可以等候它。
为的是详细说明 Promise(),它接受两个带有两个参数的回调表达式:resolve 和reject。 当表达式成功顺利完成时,他们将要返回的结果传递给resolve(),当表达式失败时,他们将错误传递给reject()。 在本教程中我们不能使用拒绝,以免使事情变得复杂,但这只是一类错误处理机制。
使用 Promise 按次序运转非堵塞表达式
现在,让他们看一看怎样使用 Promise 以正确的次序运转他们的表达式:
async function main() { login().then(function (res1) { console.log(res1) getUserPosts().then(function (res2) { console.log(res2) }) }) } main(); — Output: User logged in Got users posts当两个表达式(他们称这个表达式 1)返回两个 Promise 时,它会自动使用 .then() 方式,该方式接受两个回调表达式(他们称这个表达式 2),以便在表达式 1 顺利完成后继续执行。 此外,表达式 2 接受 1”传递给resolve()。 那些分别是 res1 和 res2。
由于使用了 Promise,他们的表达式将始终以正确的次序运转,login() 在 getUserPosts() 之前初始化并继续执行。
什么是async/await?
如果您早已了解 Promise,所以自学 async/await 将变得轻而易举,因而在继续本节之前,请确保您首先完全了解 Promise 的工作原理!
本质上,async/await 只是 JavaScript 中的句法简化器,使 Promise 看起来更整洁。 例如,上面的main表达式可以改写为:
async function main() { let res1 = awaitlogin();console.log(res1); let res2 = await getUserPosts(); console.log(res2); } main();解释触发器等候的工作原理:
当表达式返回 Promise(例如登录或 getUserPosts)时,您可以使用await关键字来确保流程等候该表达式顺利完成继续执行,接着再转到下带队。
当使用await关键字时,表达式的返回将是Promise的解析。
要在表达式内使用await句法,首先必须使用async关键字声明该表达式触发器,如上所示。
所以为什么要使用触发器等候呢? 好吧,如果您有多个 Promise 表达式,则此功能特别有用,因为在使用 Promise.then() 句法时,您最终会出现大量嵌套,从而降低了代码的可读性。 而使用 async wait 时,所有内容都整齐地写在同两个缩进上,因而人眼更容易阅读。
注意:async/await 和 Promises 之间的区别
最后,我想澄清一下 async wait 句法和 Promises 之间的区别,一早已开始我发现他们很困惑。 当您有两个需要等候的非堵塞表达式,但尚未返回 Promise 时,您不能简单地使用await 关键字并期望它成为堵塞的。 相反,您必须创建两个新的 Promise。 两个例子是使用 fs.writeFile 模块写入文件:
import fs from fs function writeToFile(filePath, contents) { fs.writeFile(filePath, contents, (err) => { if (err) { console.log(“Failed to write to file”); } else { console.log(“Successfully written to file”); } }) }async function main() { await writeToFile(“your_file.txt”, “Hello World 1”) await writeToFile(“your_file.txt”, “Hello World 2”) } main();在这里,他们有两个写入文件的表达式,并在写入顺利完成时接收回调表达式。 不过,由于 fs.writeFile() 是两个非堵塞表达式,如果他们像 main 中那样初始化 writeToFile 表达式两次,则这两个初始化将“同时”继续执行,并且await关键字将不起作用。 因而,您将不知道“Hello World 1”或“Hello World 2”最终是否是写入文件的最终字符串。
如果他们想让这个流程按次序继续执行,他们可以使用Promises,如下所示:
import fs from fs function writeToFile(filePath, contents) { return new Promise(function (res, rej) { fs.writeFile(filePath, contents, (err) => { if(err) {console.log(“Failed to write to file”); } else { console.log(“Successfully written to file”); } }) }) }async function main() { await writeToFile(“your_file.txt”, “Hello World 1”) awaitwriteToFile(“your_file.txt”, “Hello World 2”) } main();现在,“Hello World 1”将始终首先写入文件,而“Hello World 2”将始终其次写入。 希望这个示例成功地演示了非堵塞表达式怎样不需要返回 Promise,并且要创建该犯罪行为,您有时必须创建他们的 Promise。