甚麽是「Callback function」?
MDN的解释如下:
「回呼函式(callback function)是指能藉由参数(argument)通往另一个函式的函式。它会在外部函式内调用、以完成某些事情。」
我改写一下MDN上的范例:
function aFunc(name) {
alert('Hello ' + name);
}
function bFunc(callback) {
var name = prompt('报名华山论剑大会,请输入你的名字:');
callback(name);
}
bFunc(aFunc);
最後呼叫函式 bFunc (aFunc),aFunc是bFunc的callback参数,bFunc的变数name又藉由callback传入bFunc之中。所以我们可以理解「把A函式当成B函式的参数,透过B函式来呼叫它」,A函式就是一个Callback function。
解释有点抽象吗?让我们换个场景,想想之前提过的「事件监听」。
例如:看到红灯,然後踩刹车!踩刹车这个动作,在「看到红灯」这个条件满足的时候才执行。所以我们会监听「看到红灯」这个事件,一旦事件触发,就去呼叫「刹车」这个动作(函式)。这也是把「刹车」这个函式当成事件监听的参数。
还有一个常常会用到的window.setTimeout()也是callback function的经典案例:
window.setTimeout(function(){//do something},1000);
所以我们可以归纳出:
那在甚麽时候适合使用callback function呢?我想是在「控制函式执行的时机」的情境下适合使用:
假设郭靖跟欧阳克都中了毒:
let poisonA = function(){
alert('欧阳克中毒身亡!');
};
let poisonB = function(){
alert('郭靖中毒身亡!');
}
poisonA();
poisonB();
这样的执行顺序当然是先跳('欧阳克中毒身亡!')的视窗,再跳('郭靖中毒身亡!')。但是如果加上一个随机生成的等待时间,那视窗的弹跳顺序就不一定了。
let poisonA = function(){
var i = Math.random()+1;
window.setTimeout(function () {
alert('欧阳克中毒身亡!');
}, i * 1000)
};
let poisonB = function(){
var i = Math.random()+1;
window.setTimeout( () {
alert('郭靖中毒身亡!');
}, i * 1000 )
}
poisonA();
poisonB();
有时是('郭靖中毒身亡!')会先跳出来,有时是('欧阳克中毒身亡!')会先跳出来!
如果我们想确保('欧阳克中毒身亡!')比('郭靖中毒身亡!')早跳出来,可以这样写:
const poisonA = function(callback){
const i = Math.random() + 1;
window.setTimeout(function () {
alert('欧阳克中毒身亡!');
if (typeof callback === 'function'){
callback();
};
}, i * 1000)
};
const poisonB = function(){
const i = Math.random() + 1;
window.setTimeout (function() {
alert('郭靖中毒身亡!');
}, i* 1000 );
}
poisonA( poisonB );
这样欧阳克就会比郭靖还要早毒发身亡了!
但是如果中毒的人越来越多,一个函式呼叫另一个函式,一层一层包下去,就变成「回呼地狱」了。
再假设另外一个情境:
「王重阳参加华山论剑,只要打败黄药师、洪七公、段皇爷与欧阳锋,就会夺得『武功第一』的封号。但是不用去管王重阳与人决斗的先後顺序,只要与每个人都打过就可以。」
这时候我们可以这样做:
let fightProcess = []; //设一个空阵列,王重阳每次比武,都push到阵列中
let step = 4; //王重阳与4个人比武
function fightA () {
window.setTimeout(function(){
fightProcess.push('王重阳打败黄药师');
console.log('王重阳打败黄药师');
if (fightProcess.length === step){
//比较空阵列fightProcess的长度是否与step相等,如果相等,就执行ightWinner()
fightWinner();
}
},(Math.random()+1) * 1000);
};
function fightB () {
window.setTimeout(function(){
fightProcess.push('王重阳打败洪七公');
console.log('王重阳打败洪七公');
if (fightProcess.length === step){
fightWinner();
}
},(Math.random()+1) * 1000);
}
function fightC () {
window.setTimeout(function(){
fightProcess.push('王重阳打败段皇爷');
console.log('王重阳打败段皇爷');
if (fightProcess.length === step){
fightWinner();
}
},(Math.random()+1) * 1000);
}
function fightD () {
window.setTimeout(function(){
fightProcess.push('王重阳打败欧阳锋');
console.log('王重阳打败欧阳锋');
if (fightProcess.length === step){
fightWinner();
}
},(Math.random()+1) * 1000);
}
function fightWinner(){
console.log('王重阳天下武功第一,人称「中神通」');
console.log(fightProcess);
}
fightA();
fightB();
fightC();
fightD();
Promise物件是ES6之後新增的物件,照字面的解释就是「承诺」,回传的结果只有两种:「解决」与「拒绝」。
Promise物件长成这个样子:
let myPromise = new Promise((resolve, reject) =>{
resolve('解决');
reject('拒绝');
})
要在一个函式中使用Promise功能,只要让它回传一个Promise物件就行了:
function urPromise(){
return new Promise((resolve,reject) => {
//resolve()或reject()
});
}
Promise还提供了三种方法:
像刚刚那个「王重阳与四大高手比武」的过程就可以这样写:
function fightA () {
return new Promise(function(resolve,reject){
window.setTimeout(function() {
console.log('王重阳打败黄药师');
resolve('王重阳打败黄药师');
},(Math.random()+1) * 1000);
});
}
function fightB () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败洪七公');
resolve('王重阳打败洪七公');
},(Math.random()+1) * 1000);
});
}
function fightC () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败段皇爷');
resolve('王重阳打败段皇爷');
},(Math.random()+1) * 1000);
});
}
function fightD () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败欧阳锋');
resolve('王重阳打败欧阳锋');
},(Math.random()+1) * 1000);
});
}
function fightWinner(){
console.log('王重阳天下武功第一,人称「中神通」');
}
//加上.then可以做到依次执行
fightA()
.then(fightB)
.then(fightC)
.then(fightD)
.then(fightWinner);
我们在呼叫fightA()之後,用.then串接後面要执行的函式,这样我们就可以做到依顺序执行了。
来看看promise.all的情况:
function fightA () {
return new Promise(function(resolve,reject){
window.setTimeout(function() {
console.log('王重阳打败黄药师');
resolve('王重阳打败黄药师');
},(Math.random()+1) * 1000);
});
}
function fightB () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败洪七公');
resolve('王重阳打败洪七公');
},(Math.random()+1) * 1000);
});
}
function fightC () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败段皇爷');
resolve('王重阳打败段皇爷');
},(Math.random()+1) * 1000);
});
}
function fightD () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败欧阳锋');
resolve('王重阳打败欧阳锋');
},(Math.random()+1) * 1000);
});
}
function fightWinner(){
console.log('王重阳天下武功第一,人称「中神通」');
}
//不管fightA(),fightB(),fightC(),fightD()的执行顺序,只要都执行了就继续後面的程序
Promise.all([fightA(),fightB(),fightC(),fightD()])
.then(fightWinner);
Promise.all()则会等待全部的Promise函式都执行了,才会进行後面的.then函式。
然後是promise.race:
function fightA () {
return new Promise(function(resolve,reject){
window.setTimeout(function() {
console.log('王重阳打败黄药师');
resolve('王重阳打败黄药师');
},(Math.random()+1) * 1000);
});
}
function fightB () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败洪七公');
resolve('王重阳打败洪七公');
},(Math.random()+1) * 1000);
});
}
function fightC () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败段皇爷');
resolve('王重阳打败段皇爷');
},(Math.random()+1) * 1000);
});
}
function fightD () {
return new Promise((resolve,reject) =>{
window.setTimeout(() =>{
console.log('王重阳打败欧阳锋');
resolve('王重阳打败欧阳锋');
},(Math.random()+1) * 1000);
});
}
function fightWinner(){
console.log('王重阳天下武功第一,人称「中神通」');
}
//只要fightA(),fightB(),fightC(),fightD()其中之一执行,就继续执行後面的程序
//但是fightA(),fightB(),fightC(),fightD()都会执行,不会取消
Promise.race([fightA(),fightB(),fightC(),fightD()])
.then(fightWinner);
Promise.race就如同「竞赛」一样,只要有其中一个Promise函式先做到,不待其它的Promise函式完成,就直接进行.then後面的程序。但是其他的Promise函式还是会继续执行,不会取消。
I s t i o 什麽是Istio Istio是Google、IBM 和 Lyft一起开发的开源专...
近水楼台先得月 tags: IT铁人 区域性原则 有在组装电脑的人就会知道,电脑的储存装置包括记忆体...
上一篇我们学会了新增资料,但是如果资料打错了想要改的话应该怎们办呢?那这时候我们就需要用到我们的UP...
目录 JavaScript Day01 - 说明 说明与工具 JavaScript Day02 - ...