目录
本教程将介绍 JavaScript 的 回调、 承诺 和 Async/ await,并告诉你如何等待一个异步函数完成后再继续执行。
要了解什么是 Promises 和 Async/ await,我们首先要了解 JavaScript 中的 Sync 和 Async 函数是什么。
JavaScript 中的 Sync 和 Async
同步编程一次执行一条命令。当我们调用一个函数执行一个长期运行的动作时,它会停止程序,直到它完成。
JavaScript 是传统的单线程,即使是多核也是如此。我们可以让它只在一个叫做主线程的单线程上运行任务。
这样的同步行为是多线程的限制因素,但可以帮助用户编写代码而不用担心并发问题。
而在异步编程中,长期运行的动作会在主线程以外的另一个线程中执行,所以主线程的执行不会被阻塞。当该长期运行的函数完成后,主程序会被告知并获得访问结果。
JavaScript 中的大多数 I/O 基元都是非阻塞的。网络请求、文件系统操作都是非阻塞操作。被阻塞是 JavaScript 中的例外。
由于 JavaScript 是基于异步编程技术,所以有多种方法,如 回调、承诺 和 async/await,使你能够把你的函数按顺序执行。这样,一个代码块或一个函数不会在另一个特定函数完成之前被执行。
上图显示了两个函数异步和同步执行的明显变化。
在 JavaScript 中使用 回调 来等待一个函数的完成
如果我们有同步语句,那么执行这些语句之后就可以直接执行。
function one(){
console.log("I am function One");
}
function Two(){
console.log("I am function Two");
}
one();
Two();
输出:
I am function One
I am function Two
假设我们要执行两个函数,functionOne() 和 functionTwo(),这样 functionOne() 应该在执行完 functionTwo() 里面的一些异步语句后执行。
function functionOne(_callback){
// do some asynchronus work
_callback();
}
function functionTwo(){
// do some asynchronus work
functionOne(()=>{
console.log("I am a callback");
});
}
functionTwo();
在执行上述代码时,最后在控制台中打印的是 I am a callback。著名的 callback 例子是 setTimeout 函数,在超时后要执行一个处理函数。
function testCallBack(){
console.log("log before use setTimeout function");
setTimeout(()=>{
console.log("inside timeout");
},5000);
console.log("log after use setTimeout function");
}
testCallBack();
输出:
log before use setTimeout function
log after use setTimeout function
inside timeout
在 JavaScript 中使用 promises 来等待一个函数的完成
promise 是一个代表异步操作的最终实现或失败的对象,我们用一个或多个 then 语句将实现回调附加到 promise 上。我们用一个或多个 then 语句将实现回调附加到 promise 上,并在 catch 中调用错误处理回调。
doFirstThing()
.then(result => doSecondThing(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log("The final result thing"+finalResult);
})
.catch(failureCallbackfunction);
}
像上面的例子一样把 then 和 catch 语句链起来,是承诺的优势之一。我们承诺一旦 doFirstThing() 被满足,就会 doSecondThing(result)。then 的参数是可选的,但如果你必须返回一个结果,则需要。
如果出现错误,浏览器会在链的最后寻找 catch 并执行。这和著名的 try-catch 非常相似。
下面的例子将帮助我们理解 promises 链,并告诉我们如何等待一个具有异步行为的函数完成执行后才能继续执行。
var resolvedFlag = true;
let mypromise = function functionOne(testInput){
console.log("Entered function");
return new Promise((resolve ,reject)=>{
setTimeout(
()=>{
console.log("Inside the promise");
if(resolvedFlag==true){
resolve("Resolved");
}else{
reject("Rejected")
}
} , 2000
);
});
};
mypromise().then((res)=>{
console.log(`The function recieved with value ${res}`)
}).catch((error)=>{
console.log(`Handling error as we received ${error}`);
});
输出:
Entered function
Inside the promise
The function received with value Resolved
创建一个承诺可以像返回一个新的 Promise() 一样简单。Promise() 构造函数接收一个函数作为参数,它应该有两个参数-resolve 和 reject。
万一我们的事件实现了,需要返回结果,当成功完成我们正在做的事情时,我们使用 resolve() 函数。但如果发生了错误,需要处理,我们使用 reject() 将错误发送到 catch。
我们设置标志 resolvedFlag=true 来模拟 catch 中的错误处理。如果 resolvedFlag 设置为 false,则调用 reject() 函数,在 catch 块中处理错误。
输出:
Entered function
Inside the promise
Handling error as we received Rejected
使用 async/await 等待一个函数完成后再继续执行
在 JavaScript 的异步环境中,另一种等待函数执行后再继续执行的方法是使用 async/wait。
async 函数是由 async 关键字声明的函数,而在 async 函数内部只允许使用 await 关键字,用于暂停 async 函数内部的进度,直到实现或拒绝基于承诺的异步操作。
async 和 await 关键字以更简洁的风格实现了基于承诺的异步行为。
让我们来了解一下 async/await 是如何工作的。我们要等待的函数应该返回一个 Promise 类的实例,在调用它之前使用 await 关键字等待它执行。如上所述,包含 await 语句的函数必须与 async 语句一起声明。
下面的例子展示了如何等待那个基于承诺的函数完成后再继续执行。
function testAsync(){
return new Promise((resolve,reject)=>{
//here our function should be implemented
setTimeout(()=>{
console.log("Hello from inside the testAsync function");
resolve();
;} , 5000
);
});
}
async function callerFun(){
console.log("Caller");
await testAsync();
console.log("After waiting");
}
callerFun();
输出:
Caller
Hello from inside the testAsync function
After waiting