JS是单线程的
就是同一个时间只能处理一个任务。就类似生活中的去超市排队结账,正常情况下,一位收银员只能为一位顾客结账,其他顾客需要在后面排队等候。
为什么 JS 是单线程的?作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM 。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
单线程就意味着,所有任务都需要排队,前一个任务结束,才能执行后一个任务。如果前一个任务耗时很长,那么后一个任务就不得不一直等待,于是乎,JS 设计者们把所有任分成两类,同步和异步。
同步:只有前一个任务执行完毕,才能执行后一个任务 异步:当同步任务执行到某个 WebAPI 时,就会触发异步操作,此时浏览器会单独开线程去处理这些异步任务。
关于同步任务和异步任务忘深点去讲就是一次脚本执行后会按照顺序执行完成所有同步任务,而后所有异步任何会进入Event Queue, 按照Event Loop运行规则进行一次一次Loop取出任务进行线程执行。(一次EventLoop执行一个macor和所有micor)。
Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
// 创建 XMLHttpRequest 对象
const url = 'http://jsonplaceholder.typicode.com/users'
let xmlhttp
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest()
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
// 发送请求
xmlhttp.open("GET", url, true)
xmlhttp.send()
// 服务端响应
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// console.log(xmlhttp.responseText)
let obj = JSON.parse(xmlhttp.responseText)
console.log(obj)
}
}
复制代码
JavaScipt 中的许多操作都是异步的,我们把上面的Ajax封装成一个函数:
function ajax(url, callback) {
let xmlhttp
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest()
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
// 发送请求
xmlhttp.open("GET", url, true)
xmlhttp.send()
// 服务端响应
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// console.log(xmlhttp.responseText)
let obj = JSON.parse(xmlhttp.responseText)
callback(obj)
}
}
}
// call hell
ajax('static/a.json', res => {
console.log(res)
ajax('static/b.json', res => {
console.log(res)
ajax('static/c.json', res => {
console.log(res)
})
})
})
复制代码
Promise 就是为了解决“回调地狱”问题的,它可以将异步操作的处理变得很优雅。回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象promise可以支持多个并发的请求,获取并发请求中的数据这个promise可以解决异步的问题,本身不能说promise是异步的。
const promise = new Promise(function(resolve, reject) {
// ... some code
if ( /* 异步操作成功 */ ) {
resolve(value)
} else {
reject(error)
}
})
复制代码
- Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
- 处理结果正常的话,调用resolve(处理结果值),将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
- 处理结果错误的话,调用reject(Error对象),将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
- Promise存在三种状态,一旦确定状态是无法修改的。
复制代码
- Promise用法:
1. Promise.prototype.then()
2. Promise.prototype.catch()
3. Promise.resolve()
4. Promise.reject()
5. Promise.all()
> Promise.all 生成并返回一个新的 Promise 对象,所以它可以使用 Promise 实例的所有方法。参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回, 新创建的 Promise 则会使用这些 promise 的值。
6. Promise.race()
> 参数 promise 数组中的任何一个 Promise 对象如果变为 resolve 或者 reject 的话, 该函数就会返回,并使用这个 Promise 对象的值进行 resolve 或者 reject。
复制代码
var promise = new Promise(function (resolve, reject) {
resolve('传递给then的值')
})
promise.then(function (value) {
console.log(value)
}, function (error) {
console.error(error)
})
function test() {
return new Promise((resolve, reject) => {
reject(new Error('es'))
})
}
test().catch((e) => {
console.log(e.message) // es
})
// Promise.resolve()
// 让Promise直接resolve
// 就是就可以看做是语法糖
Promise.resolve(42)
new Promise(function (resolve) {
resolve(42)
})
// Promise.reject
Promise.reject(42)
// 手动实现Promise.all
Promise.all = function (values) {
return new Promise((resolve, reject) => {
let arr = [];
let i = 0;
let processData = function (key, value) {
arr[key] = value;
if (++i === values.length) {
resolve(arr);
}
};
for (let i = 0; i < values.length; i++) {
let current = values[i];
if (isPromise(current)) {
//判断传进来的是promise还是普通数据
current.then((y) => {
processData(i, y);
}, reject);
} else {
processData(i, current);
}
}
});
};
复制代码