共计 3055 个字符,预计需要花费 8 分钟才能阅读完成。
本文转自作者 Sandeep Dinesh 的文章:Callbacks, Promises and Async/Await
假设你有一个函数可以在一段随机的时间后打印一个字符串:
function printString(string){
setTimeout(
() => {
console.log(string)
},
Math.floor(Math.random() * 100) + 1
)
}
让我们尝试按顺序打印字母 A,B,C:
function printAll(){
printString(“A”)
printString(“B”)
printString(“C”)
}
printAll()
每次调用 printAll 时,您会注意到 A,B 和 C 以不同的随机顺序打印!
这是因为这些函数是异步的。每个函数按顺序执行,但每个函数都独立于它自己的 setTimeout。在开始之前,他们不会等待最后一个功能完成。
这非常烦人,所以让我们用回调修复它。
Callbacks
回调是传递给另一个函数的函数。第一个函数完成后,它将运行第二个函数。
function printString(string, callback){
setTimeout(
() => {
console.log(string)
callback()
},
Math.floor(Math.random() * 100) + 1
)
}
你可以看到,修改原始函数是非常容易的,可以使用回调。
再次,让我们尝试按顺序打印字母 A,B,C:
function printAll(){
printString(“A”, () => {
printString(“B”, () => {
printString(“C”, () => {})
})
})
}
printAll()
嗯,代码现在很丑陋,但至少它有效!每次调用 printAll 时,都会得到相同的结果。
回调的问题是它创建了一个名为“Callback Hell”的东西。基本上,你开始在函数内的函数内嵌套函数,并且开始变得非常难以阅读代码。
Promise
Promise 尝试修复这个嵌套问题。让我们改变我们的功能来使用 Promises
function printString(string){
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
Math.floor(Math.random() * 100) + 1
)
})
}
你可以看到它看起来仍然非常相似。您将整个函数包装在 Promise 中,而不是调用回调,而是调用 resolve(如果出现错误则拒绝)。该函数返回此 Promise 对象。
再次,让我们尝试按顺序打印字母 A,B,C:
function printAll(){
printString(“A”)
.then(() => {
return printString(“B”)
})
.then(() => {
return printString(“C”)
})
}
printAll()
这被称为承诺链。您可以看到代码返回函数的结果(将是 Promise),并将其发送到链中的下一个函数。
代码不再嵌套,但看起来仍然很混乱!
通过使用箭头函数的功能,我们可以删除“包装器”功能。代码变得更清晰,但仍然有很多不必要的括号:
function printAll(){
printString(“A”)
.then(() => printString(“B”))
.then(() => printString(“C”))
}
printAll()
Await
Await 基本上是 Promises 的语法糖。它使您的异步代码看起来更像是同步 / 过程代码,人类更容易理解。
该 PRINTSTRING 功能不自许的版本在所有改变。
再次,让我们尝试按顺序打印字母 A,B,C:
async function printAll(){
await printString(“A”)
await printString(“B”)
await printString(“C”)
}
printAll()
是啊 …。好多了!
您可能会注意到我们对包装函数 printAll 使用“async”关键字。这让我们的 JavaScript 知道我们正在使用 async / await 语法,如果你想使用 Await,这是必要的。这意味着你不能在全球范围内使用 Await; 它总是需要一个包装函数。大多数 JavaScript 代码都在函数内部运行,因此这不是什么大问题。
等等,这里还有更多哦
该 PRINTSTRING 函数不返回任何东西,是独立的,所有我们关心的是顺序。但是,如果您想获取第一个函数的输出,在第二个函数中执行某些操作,然后将其传递给第三个函数,该怎么办?
我们不是每次都打印字符串,而是创建一个连接字符串并传递它的函数。
Callbacks
这里是回调样式:
function addString(previous, current, callback){
setTimeout(
() => {
callback((previous + ‘ ‘ + current))
},
Math.floor(Math.random() * 100) + 1
)
}
为了使用它:
function addAll(){
addString(”, ‘A’, result => {
addString(result, ‘B’, result => {
addString(result, ‘C’, result => {
console.log(result) // Prints out ” A B C”
})
})
})
}
addAll()
不太好。
Promises
这是 Promise 风格:
function addString(previous, current){
return new Promise((resolve, reject) => {
setTimeout(
() => {
resolve(previous + ‘ ‘ + current)
},
Math.floor(Math.random() * 100) + 1
)
})
}
为了使用它:
function addAll(){
addString(”, ‘A’)
.then(result => {
return addString(result, ‘B’)
})
.then(result => {
return addString(result, ‘C’)
})
.then(result => {
console.log(result) // Prints out ” A B C”
})
}
addAll()
使用箭头函数意味着我们可以使代码更好一些:
function addAll(){
addString(”, ‘A’)
.then(result => addString(result, ‘B’))
.then(result => addString(result, ‘C’))
.then(result => {
console.log(result) // Prints out ” A B C”
})
}
addAll()
这肯定更具可读性,特别是如果你向链添加更多,但仍然是一堆括号。
Await
该功能与 Promise 版本保持一致。
并且为了使用它:
async function addAll(){
let toPrint = ”
toPrint = await addString(toPrint, ‘A’)
toPrint = await addString(toPrint, ‘B’)
toPrint = await addString(toPrint, ‘C’)
console.log(toPrint) // Prints out ” A B C”
}
addAll()
Yeah. SO MUCH BETTER~