单线程
js是单线程模式的,试想在js当中同时有多个线程,其中有一个线程修改了某一个dom元素,而另外一个线程同时删除了这个元素,浏览器就无法确定以那一个线程结果为准,为了避免这种线问题,从一开始js就被设计为了单线程模式。这里单线程是js运行环境执行代码的线程只有一个,当有多个任务的时候,排队等待依次完成。
优缺点
优点:安全,简单
缺点:假如遇到一个特别耗时的任务,其他任务就被堵塞,整个程序出现假死情况。
同步/异步
为了解决这种问题,js将执行模式分为同步(synchronous)异步(asynchronous)
同步模式(synchronous)
指的就是我们代码当中的任务依次执行,后一个任务必须等待前一个任务结束才能执行,在单线程模式下,我们大多数任务会以同步模式去运行.
简单例子:
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
}
bar()
foo()
console.log('global end')
//global begin
//bar task
//bar task
//global end
这种就是纯同步代码,就是顺序执行,如果遇到特别耗时的任务,就会堵塞。这种阻塞对于用户而言就意味着界面会卡顿,或者卡死,所以为了解决这个问题出现了异步模式。
异步模式(asynchronous)
与同步模式不同,异步模式的执行是不会等待这个任务的结束才会执行下一个任务,对于耗时操作都是开启过后就放到异步队列立即往后执行下一个任务,内部我们这个耗时任务完成过后就会自动执行我们这里传入的回调函数。
优点:解决同步线程堵塞的问题
缺点:代码执行的顺序的混乱,不像同步代码那样是顺序执行的了。
简单例子:
console.log('global begin')
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke
这里也是简单介绍开启异步的 像setTimeout是宏任务,promise是微任务,都会开启异步模式。有需要的可以再去了解下js的eventLoop(js事件循环机制),这里重点讲处理异步方法。
回调函数
就是你想做的事情,你也知道这件事你应该怎末做,但是不知道依赖的任务什么时候才完成,任务什么时候才会执行到你这个方法,最好的方法就是把这个任务写到一个函数中,交给执行者,他是知道这个任务什么时候结束的,他就可以在任务结束过后帮你去执行,你想要做的事情,那这个想要做的事情其实就可以理解为回调函数。
拿我们在拿程序当中的ajax请求为例:当我们调用ajax操作,目的就是为了拿到请求结果过后去做一些事情,例如我们将其显示到界面上,但是这个请求什么时候能完成我们并不知道,所以说我们需要把得到结果要去执行的任务定义到一个函数中,然后内部的ajax请求到数据过后,它会自动执行这个任务。
这种由调用者定义,交给执行者执行的函数,称之为回调函数。
**
简单例子:
function foo (callback) {
setTimeout(function () {
callback()
}, 3000)
}
foo(function () {
console.log('这就是一个回调函数')
console.log('调用者定义这个函数,执行者执行这个函数')
console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
})
假如你请求a数据,b在a成功返回的时候执行,c依赖b的成功执行,d依赖c.....如果是普通的会容易写成回调函数的嵌套,多层的嵌套就是回调地狱。
而promise就是一种更优的解决方案。
promise
- promise实际上就是一个对象,用来去表示一个异步任务,最终结束过后,他是成功还是失败
就像是内部对外界做出一个承诺,一开始这个承诺是一个待定的状态 Pending,最后有可能成功 fulfilled,也有可能失败 Rejected。
- 承诺状态明确过后,不管是成功还是失败,都会有相对应的任务会被自动执行,而且这种承诺会有很明显的特点,一点明确了结果过后,不可更改。
例如:你需要我去帮你发送一次ajax请求,其实就可以理解为,我承诺帮你请求一个地址,这个请求有可能成功,然后调用 onFulfiled 的回调,如果失败就会调用 onRejected 的回调。
promise基本用法
const promise = new Promise(function(resolve, reject) {
// "兑现" 承诺的逻辑
// 承诺--------达成
resolve(100)
//承诺--------失败
//reject(new Error('promise err'))
})
promise.then(function(value) {
console.log("resoled",value)
},function(err) {
console.log("rejected", err)
})
//resoled 100
你可以分别执行下resolve,和reject方法,看下它的执行结果,后面会具体说promise的resolve和reject。
promise的使用案例
function ajax (url) {
return new Promise (function (resolve, reject) {
let 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(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
Promise常见误区
function ajax(url) {
return new Promise(function(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/user.json').then(function(urls) {
ajax(urls.users).then(function(users){
ajax(urls.users).then(function(users){
// ... 还是会形成回调地狱 promise 没有了意义
})
})
},function(error) {
console.log(error)
})
就是你用了promise还发生了回调嵌套,没有用then链式调用。emm...
promise的链式调用
其实Promise最大的优势就是可以链式调用,这样就能最大程度的去避免回调嵌套。
每一个then方法他实际上都是在为上一个then返回的promise对象添加状态明确过后的回调,那这些promise会依次执行,这里添加的这些回调函数也就是从前到后依次执行。
例子:
function ajax(url) {
return new Promise(function(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()
})
}
let promise = ajax('api/user.json')
promise.then(function(value) {
console.log("1111")
return ajax('api/urls.json')
})//-> promise
.then(function(value) {
console.log("2222")
})//-> promise
.then(function(value) {
console.log("3333")
})//-> promise
.then(function(value) {
console.log("4444")
})//-> promise
.then(function(value) {
console.log("5555")
})
let promise2 = promise.then(function(res) {
console.log(res)
},function(error) {
console.log(error)
})
console.log(promise2 === promise) //false
总结:
- 每一个then方法都是在为上一个promise添加回调,
- promise的then方法会返回一个全新的promise对象(就可以使用链式调用的方式去调用then方法)
- 后面的then方法就是在为上一个then返回的promise注册回调
- 前面方法中回调函数的返回值 会作为后面then方法回调的参数
- 如果回调中返回的是一个promise对象,那后面的then方法的回调会等待它的结束
promise异常处理
- Promise执行失败,会返回onRejected这个回调函数
- 如果是在Promise执行的过程中,出现了异常,或者是我们手动抛出了一个异常,那onRejected也会被执行
我们也可以使用promise的cache方法来注册onRejected.
例子:
function ajax(url) {
return new Promise(function(resolve, reject) {
// foo() 可以捕获到异常
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/user.json') //1
.then(function onFulfilled (res) {
console.log("onFulfilled",res)
},function onRejected (error) {
console.log("onRejected",error)
})
ajax('api/user.json') //2
.then(function onFulfilled (res) {
console.log("onFulfilled",res)
})
.catch(function onRejected (error) {
console.log("onRejected",error)
})
/*
1的onRejected 只能捕获第一个then方法返回的异常
2 能捕获整个promise链条任何一个异常,都会往下传递直至被捕获
*/
//也能捕获到异常,catch 就是then方法的别名
// 相当于
// ajax('api/user.json')
// .then(function onFulfilled (res) {
// console.log("onFulfilled",res)
// })
// .then(undefined, function onRejected (error) {
// console.log("onRejected",error)
// })
promise的静态方法
promise.resolve()
快速的把一个值转换为一个promise对象
// promise.resolve() 作用就是快速把一个值转化为一个promise对象
Promise.resolve("foo")
.then(function(value) {
console.log(value)
})
// 等价于
new Promise(function(resolve, reject) {
resolve('foo')
})
// 如果primse.resolve()参数传一个promise对象 他会 返回原对象
var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) //true
// 如果我们传入的是个对象,有then方法(他是以额可以被then的对象),这样的对象耶可以作为promise来执行
Promise.resolve({
then: function(onFulfilled, onRejected) {
onFulfilled('foo1')
}
})
.then(function(value) {
console.log(value)
})
promise.reject()
快速的创建一个一定失败的promise对象.
// 无论传入什么参数都会作为 这个promise失败的原因
Promise.reject(new Error('rejected'))
.catch (function(error) {
console.log(error)
})
//rejected
Promise并行处理
promise.all()
promise.all 允许我们按照异步代码调用的顺序得到异步代码执行的顺序, 接收数组,可以是promise对象 或普通值 , 返回是个promise对象 后面可以链接then,所有成功 promise.all才是成功 有一个失败就是失败。
// 返回一个新得promise对象,必须里面的都执行完成 才会完成,
// 必须成功执行 all才会成功执行,有一个任务失败了 这个promise以失败结束
var promise = Promise.all([
ajax('/api/user.json'),
ajax('/api/posts.json')
])
// 数组里包含每个异步任务执行的结果
promise.then(function(values) {
console.log(values)
}).catch(function onRejected (error) {
console.log("onRejected",error)
})
注意:promise.all() 等待所有的任务结束后才结束。
promise.race()
Promise.race() 跟着所有任务当中第一个任务一起结束。
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('timeout')), 500)
})
Promise.race({
request,
timeout
})
.then( value => {
console.log(value)
})
.catch(error => {
console.log(error)
})
promise执行时序
Promise 当中并没有任何的异步操作,那他的回调函数仍然会进入到回调队列当中去排队,也就是说我们必须要等待当前所有的同步代码执行完成,过后才会去执行 Promise 当中的回调。
js的执行次序 主线程->微任务队列->宏任务队列。
在主线程执行完后 会立即执行微任务队列(从头到尾)中间遇到宏观任务就加到宏观任务队列的尾部,执行完微队列,再执行宏任务。
而promise就是微任务,setTimeout等是宏任务。
看个例子:
console.log('global start')
Promise.resolve()
.then(() => {
console.log('promise')
})
console.log('global end')
/**
* global start
* global end
* promise
* */
//=================================
console.log('global start')
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
console.log('global end')
/**
* global start
* global end
* promise 1
* promise 2
* promise 3
* promise 4
* */
//==============================
console.log('global start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
console.log('global end')
/**
* global start
* global end
* promise 1
* promise 2
* promise 3
* promise 4
* setTimeout
* */
完结撒花~😊
读书不觉已春深,一寸光阴一寸金