JavaScript异步学习 原创
JavaScript异步学习
学习下JavaScript异步,边学边记录。分享如果同样需要学习的你。
1、JavaScript Callbacks
call back有回电话,招回来,喊回来的意思。在编程语言中,指的是回调函数。回调函数是一种作为参数传递给其他函数的函数,这种技术就允许一个函数调用另外一个函数。回调函数能在另外一个函数完成后执行。
1.1 Function Sequence 函数执行顺序
JavaScript函数按调用的顺序执行,而不是定义的顺序,下面的例子会先执行myFirst(),然后执行mySecond(),最后名称为"demo"的页面元素,会展示为"Goodbye"。如果想体验运行下,可以访问https://www.w3schools.com/Js/tryit.asp?filename=tryjs_callback1。
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myFirst() {
myDisplayer("Hello");
}
function mySecond() {
myDisplayer("Goodbye");
}
myFirst();
mySecond();
1.2 Sequence Control 顺序控制
有的场景下,需要控制什么时候执行一个函数。假设需要进行一个计算,然后展示结果。你需要先调用myCalculator
函数,保存结果,然后调用另外一个函数myDisplayer
展示结果,如下:
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2) {
let sum = num1 + num2;
return sum;
}
let result = myCalculator(5, 5);
myDisplayer(result);
或者,你也可以这样写:
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2) {
let sum = num1 + num2;
myDisplayer(sum);
}
myCalculator(5, 5);
上面第一个例子的问题是,不得不写两个函数来展示结果。第二个例子的问题是,在计算函数里也一直会展示结果。下面就看,如何使用回调函数来解决这些问题。
2、JavaScript Callbacks
A callback is a function passed as an argument to another function. 回调是一种作为参数传递给另外一个函数的函数。
可以使用回调函数myCallback
作为参数,调用计算函数myCalculator
。计算函数在完成计算后,执行回调函数来展示结果。
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
在这个例子中,myDisplayer
是个回调函数,作为参数传递给计算函数myCalculator
。
注意:当把函数作为参数传递时,不需要括号,如下:
Right: myCalculator(5, 5, myDisplayer);
Wrong:
myCalculator(5, 5, myDisplayer());
再看一个例子,在①处定义了一个数组。在④处定义一个函数,它有2个参数,第一个是个数组,第二个是个回调函数。会遍历数组,把每一个数组元素作为参数传递给回调函数,如果回调函数返回true,则把数组元素存入新数组,最后返回新数组。②处调用④处定义的函数,把给定的数组,使用回调函数过滤了一遍,该回调函数会过滤正数。③处把过滤后的数组传递给网页元素。
// Create an Array
① const myNumbers = [4, 1, -20, -7, 5, 9, -6];
// Call removeNeg with a callback
② const posNumbers = removeNeg(myNumbers, (x) => x >= 0);
// Display Result
③ document.getElementById("demo").innerHTML = posNumbers;
// Keep only positive numbers
④ function removeNeg(numbers, callback) {
const myArray = [];
for (const x of numbers) {
if (callback(x)) {
myArray.push(x);
}
}
return myArray;
}
在该例子中,(x) => x >= 0
是个回调函数,作为参数传递给removeNeg()
。回调函数也可以接受参数。
3、When to Use a Callback? 什么时候使用回调
这些例子没有什么意思,主要演示下回调函数的语义。回调函数真正有价值的地方是用在异步函数里,一个函数不得不等待另外一个函数,如等待文件上传。关注后文了解异步函数。
4、Waiting for a Timeout
上文解释了回调函数的语义,在编程实践中,回调经常和异步函数一起使用,在JavaScript中典型的例子为setTimeout()
。
当使用 JavaScript函数 setTimeout()
时,可以指定一个时间到期后执行的回调函数。
setTimeout(myFunction, 3000);
function myFunction() {
document.getElementById("demo").innerHTML = "I love You !!";
}
在这个例子中,myFunction
作为回调函数使用,作为参数传递给函数 setTimeout()
。3000ms即3秒后,回调函数才会被调用。
除了传递函数名称,也可以传递完整的函数,如下:
setTimeout(function() { myFunction("I love You !!!"); }, 3000);
function myFunction(value) {
document.getElementById("demo").innerHTML = value;
}
5、Waiting for Intervals
当使用JavaScript函数 setInterval()
时, 可以指定回调函数在指定的每个时间间隔执行。如下:
setInterval(myFunction, 1000);
function myFunction() {
let d = new Date();
document.getElementById("demo").innerHTML=
d.getHours() + ":" +
d.getMinutes() + ":" +
d.getSeconds();
}
在该例子中,myFunction
是个回调函数,作为参数传递给setInterval()
。实现了每一秒钟,执行一次回调函数,完成了一个时钟的效果。
6、Callback Alternatives 什么时候使用回调
在异步编程时,JavaScript程序可以启动长时间的任务,然后平行的去运行其他任务。
但是,异步程序难以编写和调试。在现代异步JavaScript方法不使用回调,替代的是,使用Promise。后续会继续介绍。
7、JavaScript Promise Object
JavaScript对象用于承诺一个结果,包含生产代码"Producing code"和消费代码"Consuming code",Promise就是链接两者的对象。
生产代码"Producing code"包含在创建Promise对象的函数内,就是需要一定执行时间的任务,消费代码"Consuming code"用于调用Promise对象,等待Promise有结果后才会执行then函数。
JavaScript Promise语义如下,其中myResolve, myReject为函数参数名称,也可以有其他的参数命名,第一个参数当生产代码"Producing code"需要成功的结果时调用,第二个参数返回失败的结果时调用。在then函数里,value和error属于Promise返回的成功或失败的结果。
let myPromise = new Promise(function(myResolve, myReject) {
// "Producing Code" (May take some time)
myResolve(); // when successful
myReject(); // when error
});
// "Consuming Code" (Must wait for a fulfilled Promise)
myPromise.then(
function(value) { /* code if successful */ },
function(error) { /* code if some error */ }
);
当生产代码"Producing code"获得结果时,会调用下面的回调函数中的一个,成功或失败分别调用不同的回调函数:
Result | Call |
---|---|
Success | myResolve(result value) |
Error | myReject(error object) |
8、Promise Object Properties
Promise对象支持2个属性: state
和 result
。
一个JavaScript Promise对象的状态为下面中的一个:
-
Pending
-
Fulfilled
-
Rejected
当JavaScript Promise对象处于 “pending” (working)时,result结果为undefined。
当JavaScript Promise对象处于 "fulfilled"时,result结果为一个值value。
当JavaScript Promise对象处于 “rejected” 时,result结果是一个error对象。
myPromise.state | myPromise.result |
---|---|
“pending” | undefined |
“fulfilled” | a result value |
“rejected” | an error object |
不能访问Promise的state和result属性。必须使用Promise的方法来处理promise。
9、Promise How To
使用Promise对象的示例如下:
myPromise.then(
function(value) { /* code if successful */ },
function(error) { /* code if some error */ }
);
Promise.then()需要两个参数,一个回调参数用于成功,一个回调用于失败。两个都是可选的,也可以只提供其中的一个。
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
let myPromise = new Promise(function(myResolve, myReject) {
let x = 0;
// The producing code (this may take some time)
if (x == 0) {
myResolve("OK");
} else {
myReject("Error");
}
});
myPromise.then(
function(value) {myDisplayer(value);},
function(error) {myDisplayer(error);}
);
在介绍Promise之后,我们继续看Async和Await。
10、Async Syntax
关键字async
用在一个函数前,会让函数返回一个Promise。
async function myFunction() {
return "Hello";
}
等价于:
function myFunction() {
return Promise.resolve("Hello");
}
使用Promise的完整例子:
async function myFunction() {
return "Hello";
}
myFunction().then(
function(value) {myDisplayer(value);},
function(error) {myDisplayer(error);}
);
也可以简化,只期望一个正常值,不返回error时,示例为:
async function myFunction() {
return "Hello";
}
myFunction().then(
function(value) {myDisplayer(value);}
);
11、Await Syntax
await
关键字只能用在async
函数内,该关键字让函数暂停执行,等待resolved的promise对象,然后再继续执行。
let value = await promise;
示例如下:
async function myDisplay() {
let myPromise = new Promise(function(resolve, reject) {
resolve("I love You !!");
});
document.getElementById("demo").innerHTML = await myPromise;
}
myDisplay();