js 采用单线程原因
js 采用单线程的优缺点
js 把执行模式分为 同步和异步模式进行执行
单线程采用排队执行的机制,配合EventLoop来对任务进行等待和发送
js 引擎维护了一个正在执行的表 记录当前做的事情
工作表中任务清空,结束任务
延迟称为阻塞 ,即表现为界面卡顿和卡死现象, 异步模式的存在是为了解决耗时操作。
console.log('start');
function bar(){
console.log('bar');
}
function foo(){
console.log('foo');
bar()
}
foo();
console.log('end');
不会等待上一个任务的结束,开启过后立即执行下一个任务,耗时任务的后续逻辑一般通过回电函数的方式来定义
console.log('start');
setTimeout(()=>{
console.log('timer1');
},1800)
setTimeout(()=>{
console.log('timer2');
setTimeout(()=>{
console.log('inner');
},1000)
},1000)
console.log('end');
// start
// end
// timer2
// timer1
// inner
异步实现方式 回调函数 事件机制 和发布订阅 ,事件机制 和发布订阅 也是基于回调函数的
function foo(cb) {
setTimeout(()=>{
cb()
},1000)
}
foo(()=>{
console.log('回调');
console.log('调用者定义,执行者执行');
console.log('就是调用者告诉执行者异步任务结束后应该做什么');
})
// 回调地狱
Promise 是为了不产生回调地狱的
commonjs 提出了 Promise 的规范
Promise 是一个对象 - 有三种状态, 开始 pending 成功 Fulfillted 失败 Rejected
开始 pending 可能成功 Fulfillted 执行成功后的方法 onFulfillted 可能失败 Rejected 执行失败的方法 onRejected
基本示例
const promise = new Promise((resolve,reject)=>{
resolve(100)
})
promise.then(value=>{
console.log(value);
},err=>{
console.log(err);
})
console.log('end');
function ajax(url) {
return new Promise((resolve,reject)=>{
var xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json'
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('./api/users.json').then(res=>{
console.log(res);
},err=>{
console.log(err);
})
// 嵌套使用 Promise 是最常见的误区
ajax('/api/urls.json').then(function (urls) {
ajax(urls.users).then(function (users) {
ajax(urls.users).then(function (users) {
ajax(urls.users).then(function (users) {
ajax(urls.users).then(function (users) {
})
})
})
})
})
function ajax(url) {
return new Promise((resolve,reject)=>{
var xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json'
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('./api/users.json')
.then(res=>{
console.log(res);
return ajax('./api/users.json')
})
.then(res=>{
console.log(res);
return ajax('./api/users.json')
})
.then(res=>{
console.log(res);
return ajax('./api/users.json')
})
.then(res=>{
console.log(res);
return 'foo'
})
.then(res=>{
console.log(res);
})
因为 Promise 对象的then方法返回的是一个新的Promise对象,所以不会进入回调地狱,但是需要使用 then 方法写链式编程
ajax('./api/users.json')
.then(function onFulfilled(res) {
console.log('onFulfilled', res)
}).catch(function onRejected(err) {
console.log('onRejected', err);
})
ajax('./api/users.json')
.then(function onFulfilled(res) {
console.log('onFulfilled', res);
}).then(function onFulfilled(res) {
console.log('onFulfilled', res);
})
Promise 发生异常会一直向后传递直到被捕获,catch 像给整个联调注册回调更合适
捕获异常不建议注册全局的异常捕获事件,而是应该在每一个 Promise 对象的后面注册 catch 去捕获当前的 Promise 对象是否存在异常
function ajax(url) {
return new Promise((resolve,reject)=>{
var xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json'
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
// 使用catch注册失败回调
ajax('./api/users.json')
.then(res=>{
console.log(res);
return ajax('./api/users.json')
})
.catch(err=>{
console.log(err);
})
// then(onRejected) === then(undefined,onRejected)
ajax('./api/users.json')
.then(res=>{
console.log(res);
return ajax('./api/users.json')
}).then(undefined,(err)=>{
console.log(err);
})
// 全局捕获 promise异常
window.addEventListener('unhandlerejection',event=>{
const {
reason,promise} = event
console.log(reason,promise);
event.preventDefault();
},false)
// node中的方式
process.on('unhandledRejection',(reason,promise)=>{
console.log(reason,promise);
})
Promise 的静态方法
// 方式一
Promise.resolve('foo')
.then(function (value) {
console.log(value);
})
// 方式二
new Promise(function (resolve, reject) {
resolve('foo')
})
// 在把一个字符串转为promise对象上时相等的
如果传入的是一个 promise 对象会原样返回
Promise.reject(new Error('reject'))
.catch(function (err) {
console.log(error);
})
Promise 并行执行
all 方法 可以统一管理所有的 promise 对象,所有的 promise 对象都成功了会返回一个 promis 对象,如果失败了就会返回失败的对象
race 方法 可以把 promise 对象组合 但是不会等待其他的 promise 对象执行,只要有一个执行结束成功就成功了
promise 和 setTimeout 同时存在会先执行 promise 后执行 setTimeOut
排队的队列也就是回调队列 ,回调队列中的任务称为宏任务
宏任务执行中有一些临时的需求的时候,可以作为一个新的宏任务去队列的最后面进行排队,也可以作为当前任务的微任务,在当前宏任务执行结束后立即执行
promise 回调作为微任务执行的 本轮调用的末尾进行执行,但是 setTimeout 是以宏任务进入到了列队的末尾进行排队的,所以后执行 setTimeout
console.log('start');
setTimeout(()=>{
console.log('setTimeout');
},0)
Promise.resolve().then(()=>{
console.log('promise');
}).then(()=>{
console.log('promise1');
})
console.log('end');
function * foo(){
console.log('start');
try {
const res = yield 'foo'
console.log(res);
} catch (error) {
console.log(error);
}
}
const generator = foo();
const res = generator.next();
console.log(res);
// generator.throw(new Error('xxx error'))
function * main () {
try {
const users = yield ajax('/api/users.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls11.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
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)是一个异步方案的库 但是有了async 和 await 之后就很少用co了
co(main)
// async 和genarator的区别
// 1. 写法上在function前加async 去掉 函数名前的* ,
// 2. yield 换成 await
// 3. 函数调用返回了一个promise对象 不需要用co库
// 4. await只能出现在async的内部去使用
async function main () {
try {
const users = await ajax('/api/users.json')
console.log(users)
const posts = await ajax('/api/posts.json')
console.log(posts)
const urls = await ajax('/api/urls.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
// co(main)
const promise = main() // main函数调用返回了一个promise对象