文章内容输出来源:拉勾大前端高薪训练营
单线程 JavaScript 异步方案:
1、采用单线程模式工作的原因
主要是跟他的设计初衷有关,最早它就是运行在浏览器端的脚本语言,目的为了实现页面上的交互,而实现页面交互的核心就是dom操作,这就决定了它必须使用单线程模型,否则就会出现多线程同步问题。
JS 执行环境中负责执行代码的线程只有一个。
- 优点:安全简单
- 缺点:无法同时处理大量耗时任务,耗时任务会阻塞主程序的执行。
如果遇到一个特别耗时的任务,后面的任务都必须排队等待这个业务的结束,导致整个程序的执行被拖延,出现假死的情况。
为了解决js 耗时任务阻塞执行的问题,JavaScript 将任务的执行模式分为两种:同步模式(Synchronous)和 异步模式(Asynchronous)
2、同步模式 和 异步模式
同步模式:按照代码的顺序,排队等待执行
异步模式:不会等待这个任务的结束才开始下一个任务,对于耗时操作,开启之后,就立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义,在内部耗时任务完成后就自动执行回调函数。
这种模式很重要,因为 单线程的 JavaScript 语言就无法同时处理大量耗时任务。
- 难点:代码的执行顺序混乱,所以需要理解和习惯,多看、多练、多思考
js是单线程,但是浏览器不是单线程,通过js调用的某些内部api不是单线程的。
例如:倒计时器
它内部就有一个单独的线程去负责倒数,时间到了之后将回调放入消息队列
我们说的单线程是执行我们代码的是单线程,但是内部api会用单独的线程执行这些等待的操作
同步API和异步API的区别
同步api 的特点:代码执行完才继续往下走。例如: console.log
异步api的特点:下达任务开启的指令之后就继续往下走,代码不等待任务结束,例如:setTimeout
3、回调函数
- 是所有异步编程方案的根基
- 可以理解为一件你想要做的事情
- 调用者定义,执行者执行
4、Promise:一种更优的异步编程统一方案
详细说明:Promise:一种更优的异步编程统一方案 学习笔记
promise 就是一个对象,表示一个异步任务最终结束过后是成功还是失败,就像是内部对外界做的一个承诺,明确结果之后不能更改。
- 目的:为了避免回调地狱的问题,为异步编程题提供一种更合理,更强大的统一解决方案
- 注意:即便 promise 中没有任何的异步操作,then方法中指定的回调函数,仍然会进入到回调队列中排队,也就是必须要等到同步代码执行完了,这里才会执行。
const promise = new Promise(function( resolve, reject){
//这里兑现“承诺”
resolve(100); //承诺达成
//reject(new Error('promise rejected')); //承诺失败
});
promise.then(function(value){
console.log(value);
}, function(value){
console.log(value);
})
console.log('end');
//结果:end、100
5、Generator 异步方案
Generator 函数是ES2015 提供 的生成器函数。
- 它就是在普通的函数的基础之上多了一个*,调用一个生成器函数并不会立即去执行这个函数,而是得到一个生成器对象,指导我们手动调用这个对象的next方法,这个函数的函数体才会开始执行。
- 在函数内部可以随时使用 yield 关键词 向外返回一个值,可以在 next 方法返回对象中的value拿到这个返回的值,这个返回的对象中还有一个 done 属性,用来表示这个生成器是否全部执行完了
- yield 关键词 不会像 return 语句一样 立即结束 函数的执行,只是 暂停 生成器函数的执行,直到外部再次调用生成器对象的next 方法时,它就会继续从yield 位置往 下执行。
- 调用 next 时 如果传递参数,参数会作为 yield 的返回值,可以在 yield 的左边接收到这个值
- 调用生成器对象的 throw 方法,可以对生成器函数内部抛出异常,继续执行会得到异常,可以通过 try catch 得到这个异常
//Generator 配合 Promise 的异步方案
function ajax(url){
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function (){ //请求完成 也就是 readyStatus = 4 才会执行
if(this.status == 200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
}
xhr.send();
})
}
function * main (){
try {
// users.json : [ { "name": "张三", "age": 13 },{ "name": "里斯", "age": 20}]
const users = yield ajax('/api/users.json');
console.log(users);
// urls.json : [ "/api/urls.json", "/api/users.json"]
const urls = yield ajax('/api/urlss.json');
console.log(urls);
} catch (error) {
console.log(error);
}
}
// const res = g.next();
// res.value.then( data => {
// const res2 = g.next(data);
// console.log(res2);
// if(res2.done) return
// res2.value.then( data => {
// const res3 = g.next(data);
// console.log(res3);
// })
// })
function co(generator){
const g = generator();
// 递归
function handleResult(result){
if(result.done) return; //生成器函数结束
result.value.then( data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next());
}
co(main)
执行结果
6、Async、Await 语法糖
它是 语言层面的异步编程标准,提供扁平化的异步编程体验。
它就是生成器函数更方便的语法糖。语法上 跟 Generator 函数类似,只需要把生成器函数 修改成 async 的普通函数,yield 替换成 await。
- async 返回一个 promise 对象,更加利于整体代码的控制
- async 中的 await 只能出现在 async 中,不能在外部单独使用
//Generator 配合 Promise 的异步方案
function ajax(url){
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function (){ //请求完成 也就是 readyStatus = 4 才会执行
if(this.status == 200){
resolve(this.response);
}else{
reject(new Error(this.statusText));
}
}
xhr.send();
})
}
async function main (){
try {
const users = await ajax('/api/users.json');
console.log(users);
const urls = await ajax('/api/urlss.json');
console.log(urls);
} catch (error) {
console.log(error);
}
}
main()
执行结果同上