文章目录
callback 回调函数
定义:被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
为什么要使用回调函数?
回调函数分为同步回调函数和异步回调函数。我们常用的也是异步回调函数,比如网络请求后台数据,我们不知道数据什么时候返回,所以用异步回调函数处理返回结果。
同步回调函数:会立即执行的函数
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
let name = prompt('请输入你的名字。');
callback(name);
}
processUserInput(greeting);
异步函数:不会立即执行的函数
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
let name = prompt('请输入你的名字。');
// 延迟一秒后执行
setTimeout(function(){ callback(name); }, 1000);
}
processUserInput(greeting);
怎么使用回调函数?
从上面的示例可以看到,回调函数的使用很简单,只需要在函数的参数里面填入callback
函数,然后在函数体里面合适的位置调用callback
函数即可。
回调地狱
在使用JavaScript
时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况被成为回调地狱。
看看下面的代码:
// 回调方法
let stepFunc= function (name, callback) {
setTimeout(function () {
console.log(name);
callback();
}, 1000);
}
// 回调嵌套
stepFunc("第一步", function () {
stepFunc("第二步", function () {
stepFunc("第三步", function () {
console.log("结束");
});
});
});
// 输出:
> "第一步"
> "第二步"
> "第三步"
> "结束"
Promise
解决什么问题
Promise
是一个对象,它代表了一个异步操作
的最终完成或者失败。
不同于callback
函数,在使用 Promise
时,会有以下约定:
- 在本轮 函数 运行完成之前,
promise
的回调函数是不会被调用的。 - 即使异步操作已经完成(成功或失败),在这之后通过
then()
添加的回调函数也会被调用。 - 通过多次调用
then()
可以添加多个回调函数,它们会按照插入顺序进行执行。
如何使用
let stepFunc = function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(name);
resolve(); //在异步操作执行完后执行 resolve() 函数
}, 1000);
});
}
stepFunc("第一步")
.then(function () {
return stepFunc("第二步"); //仍然返回一个 Promise 对象
}).then(function () {
return stepFunc("第三步");
}).then(function () {
console.log('结束');
}).catch(function (err) {
console.log(err);
}).finally(function (err) {
console.log('finally');
})
// 输出
> "第一步"
> "第二步"
> "第三步"
> "结束"
> "finally"
Promise多异步任务合并用法
Promise.all(iterable)
这个方法返回一个新的
promise
对象,该promise
对象在iterable
参数对象里所有的promise
对象都成功的时候才会触发成功,一旦有任何一个iterable
里面的promise
对象失败则立即触发该promise
对象的失败。Promise.all
方法常被用于处理多个promise
对象的状态集合。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
Promise.race(iterable)
当
iterable
参数里的任意一个子promise
被成功或失败后,父promise
马上也会用子promise
的成功返回值或失败详情作为参数调用父promise
绑定的相应句柄,并返回该promise
对象。
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// expected output: "two"
Promise.all 和 Promise.race 的区别
Promise.all
只要集合里面有一个Promise
返回失败则整体返回失败,集合中的返回结果是有顺序的。Promise.race
返回结果是集合中某个执行时间最短的Promise
的结果。
Promise 和 callback 的区别
- 纯粹的
callBack
回调因为剥夺了函数的回调能力,所以当函数的调用层数很多的时候,需要层层传递callBack
Promise
不需要层层传递callBack
,因为它使函数具备了return
的能力。使得函数调用变成了链式写法。不仅改善了可读性,而且对于多层嵌套的回调函数尤其方便。- 对于多个异步需要合并的情况下,用纯粹的
callback
是相当麻烦的,但是Promise
很好的解决这个问题 Promise
是一个对象,不同于函数,对象能保留状态,而函数在被调用需要马上返回状态。
async/await
async
函数是使用async
关键字声明的函数。async
函数是AsyncFunction
构造函数的实例, 并且其中允许使用await
关键字。async
和await
关键字让我们可以用一种更简洁的方式写出基于Promise
的异步行为,而无需刻意地链式调用Promise
。
解决什么问题
用同步的方式写出异步的代码。
如何使用
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}
// 使用时需要做捕获异常
try{
asyncCall();
}catch(e){
console.error(e)
}
// 先输出 calling 然后隔两秒输出 resolved
> "calling"
> "resolved"
async/await 和 promise 的区别
Promise
是异步编程的一种解决方案async
和await
是语法糖,作用是让我们能够像写同步代码一样编写异步代码async
用来声明一个异步方法,返回的是一个promise
对象await
结果是promise
的then
取值await
只能在async
方面的里面使用,让后面的执行语句或方法要等待当前await
方法的结果后才能再执行。
总结
处理异步代码的过程中我们使用了callback
回调函数,但是回调函数嵌套过多会影响代码的可读性也不方便维护,所以ES6
中新增了Promise
特性可以解决异步问题,但是代码样式看起来还是有异步的味道,故ES8
新增了async/await
特性,让我们的代码可以像同步代码一样写异步代码。