异步编程
异步编程
一:同步与异步
同步:Javascript是一种单线程语言。单线程也就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。同步具体表现为:JS文件中的代码从上往下连续执行。
如果有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
异步:就是代码执行的顺序并不是按照从上到下的顺序一次性一次执行,而是在不同的时间段执行,一部分代码在“未来执行”。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有”堵塞“效应。
二:异步编程方法
回调函数(Callback)
回调函数是异步操作最基本的方法。如下ajax网络请求执行成功后调用success()
$.ajax({url:"/url",success:function(result){
//逻辑代码
}});
如果多个请求存在依赖性,你可能就会写出如下代码产生回调地狱,不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱。
ajax('url1', function(){
A();
ajax('url2', function(){
B();
ajax('url3', function(){
C();
}
}
});
promise
Promise 对象为异步而生,只是个异步操作容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的最终完成或失败状态, 及异步操作结果值。用链式调用解决了回调地狱。
Promise
对象代表一个异步操作,有三种状态:
-
pending
进行中 -
fulfilled
已成功 -
rejected
已失败对象的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
用法:Promise构造函数
构造函数主要是用于包装还未支持promises的函数。
语法:var promise = new Promise( function(resolve, reject) {...} /* executor */ );
语法点1:executor函数
带有 resolve
和 reject
两个参数。Promise构造函数执行时立即调用executor
函数, resolve
和 reject
两个函数作为参数传递给executor
。
1.executor函数执行一些异步操作:
异步操作成功:调用resolve函数将 状态改成fulfilled
Promise.resolve(value):
返回一个状态由给定value决定的Promise对象:
该value为空,基本类型或者不带then方法的对象返回的Promise对象状态为fulfilled,并且将该value传递给对 应的then方法;
如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定。如果你 不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value 以Promise对象形式使用。
异步操作失败:调用reject 函数将promise的状态改为rejected。
如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
Promise.reject(reason):
返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法
2.executor函数中执行一些同步操作:
执行顺序:executor函数中的同步代码会优于函数内的异步代码执行。当调用resolve/reject方法改变promise的状态后then方法才会被调用。
<script>
const promise = new Promise((resolve, reject) => {
console.log('同步打印1:promise start');
// 异步操作
setTimeout(()=>{
for (let index = 0; index < 2; index++) {
console.log('Promise' + index);
}
resolve('promise done');
},1000);
console.log('同步打印2:promise end');
});
console.log('同步打印3');
promise.then((v)=>{console.log(v)});
/*上面代码的打印执行顺序
同步打印2:promise end
同步打印3
Promise0
Promise1
promise done
*/
</script>
语法点2:then()
then() 方法返回一个 Promise
。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
Promise.prototype.then(onFulfilled, onRejected)
pending 状态的 Promise 对象变为fulfilled 状态时传递一个值给相应的状态处理方法,或者变为rejected状态 并传递失败信息。
1.当其中任一种情况出现时,Promise 对象的 then
方法绑定的处理方法(handlers )就会被调用。
2.then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型,为then的回调函数。
3.当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,该方法有一个参数,即接受的最终结果。,
当Promise状态为rejected时,调用 then 的 onrejected 方法, 该函数有一个参数,即拒绝的原因。
所以在异步操作的完成和绑定处理方法之间不存在竞争,异步完成在前,then方法执行在后。
4.then方法返回值:返回一个Promise。返回的promise状态及返回值由then回调函数返回值决定。详情:点击这。因为返回了promise,所以可以被 链式调用。
promise,then调用时序总结:promise容器的异步操作执行完毕,调用resolve/reject改变promise的状态,promise绑定的then()会在主堆栈结束后调用(主线程的时候then()
中的函数被置入了一个微任务队列)。then中的回调函数(onfulfilled /onrejected)执行后的返回的promise对象的状态及返回值由then回调函数(onfulfilled /onrejected)返回值决定。
<script>
const resolvedProm = Promise.resolve(33);
console.log(resolvedProm );
setTimeout(() => {
console.log('第三:',thenProm);
});
let thenProm = resolvedProm.then(value => {
console.log("第二:在主堆栈结束后调用,优于任务进程。 接收和返回的值为:" + value);
setTimeout(function() {
console.log("第五");
}, 1);
return value;
});
console.log('第一:',thenProm);
setTimeout(() => {
console.log('第四',thenProm);
});
/* 上面的代码会依次打印出:
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
第一:Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} 因为 resolvedProm异步调用then方法,同步时thenProm还未被赋值
第二:在主堆栈结束后调用,优于其他任务进程。 接收和返回的值为:33
第三:Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
第四:Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
第五
*/
</script>
语法点3:catch()
catch() 方法返回一个Promise,并且处理拒绝的情况,用于promise组合中的错误处理。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
p.catch(function(reason) {
//reason为拒绝的原因。
});
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success!"
throw 'oh, no!';
}).catch(function(e) {
console.log(e); // "oh, no!"
}).then(function(){
console.log('catch()方法后面的链式继续执行');
}, function () {
console.log('Not fired due to the catch');
});
/*打印结果
Success
oh, no!
catch()方法后面的链式继续执行
*/
注意:不被catch捕获的错误
// 在异步函数中抛出的错误不会被catch捕获到
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // 不会执行
});
// 在resolve()后面抛出的错误会被忽略
var p3 = new Promise(function(resolve, reject) {
resolve();
throw 'Silenced Exception!';
});
p3.catch(function(e) {
console.log(e); // 不会执行
});
Promise.all() :详情点击
发起并行操作,然后等多个操作全部结束后进行下一步操作,如下:
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
promise解决了异步回调嵌套,使编写的代码扁平化。但是没有摆脱回调函数。
async/await
异步(async)函数是 ES7 的一个新的特性,它结合了 Promise,让我们摆脱 callback 的束缚,直接用类同步的“线性”方式,写异步函数。
async声明异步函数:
异步函数声明:只需在普通函数前添加一个关键字 async
即可.
返回值:async函数返回一个promise对象。返回的Promise
对象会运行执行(resolve)异步函数的返回结果 (return)或者运行拒 绝(reject)——如果异步函数抛出异常的话。
单运行一个async函数:还是同步的运行
async function asynFunction(){
console.log('asynFunction begin');
for (let index = 0; index < 2; index++) {
console.log('asynFunction process ' + index);
}
console.log('asynFunction end');
}
console.log('1');
console.log(asynFunction())
console.log('2');
/*打印顺序
1
asynFunction begin
asynFunction process 0
asynFunction process 1
asynFunction end
Promise {<resolved>: undefined} async函数返回一个promise对象,没有return则返回值为undefined
2
*/
await 堵塞作用:
用来等待异步执行结果的,阻塞await语句后面的代码,把异步改成一个同步的写法。
await在使用时,后面需要跟一个Promise对象,如果不是,会被转为Promise对象。
function b(arg){
return new Promise((resolve, reject) => {
resolve('你好,' + arg);
})
}
async function asynFunction(){
var aResult = await '中国';
var bResult = await b(aResult);
console.log(bResult);
}
console.log('1');
asynFunction();
console.log('2');
/*
1
2
你好,中国
*/