每一个异步请求立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程。而Promise的then方法,允许指定回调函数,在异步任务完成后调用
下面的setTimeout()可以代替理解为一个ajax请求,所以ajax请求同理
function a() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('执行任务a');
resolve('执行任务a成功');
}, 1000);
});
}
function b(value) {
console.log(value)
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('执行任务b');
resolve('执行任务b成功');
}, 2000);
});
}
function c() {
console.log('最后执行c')
}
a().then(b).then(c);
很多像promise的异步封装方法,比如angular1.x内置封装的$http方法,如下,可以实现多个回调的链式调用,避免了金字塔式的回调地狱
//1
$http({
method: 'GET',
url: 'news.json',
}).then(function successCallback(response) {
console.log(response)
}, function errorCallback(response) {
console.log(response)
})
//2
.then(function() {
return $http({
method: 'GET',
url: 'data.json',
})
}).then(function(data) {
console.log(data)
})
//3
.then(function() {
setTimeout(function() {
console.log("定时器")
}, 1000)
})
await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。
如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
function a() {
return new Promise(function(resolve){
setTimeout(()=>{
console.log("a")
resolve()
},1000)
});
}
function b() {
return new Promise(function(resolve){
setTimeout(()=>{
console.log("b")
resolve()
},1000)
});
}
function c() {
return new Promise(function(resolve){
setTimeout(()=>{
console.log("c")
resolve()
},1000)
});
}
//ES6
a()
.then(b)
.then(c);
//ES2017
await a();
await b();
await c();
await等待的虽然是promise对象,但不必写.then(..)
,直接可以得到返回值,所以使用await就没有了多个then的链式调用
var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, time);
})
};
var start = async function () {
// 在这里使用起来就像同步代码那样直观
console.log('start');
await sleep(3000);
console.log('end');
};
start();
$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作,done()相当于success方法,fail()相当于error方法。采用链式写法以后,代码的可读性大大提高,deferred对象的一大好处,就是它允许你自由添加多个回调函数
$.when(function(dtd) {
var dtd = $.Deferred(); // 新建一个deferred对象
setTimeout(function() {
console.log(0);
dtd.resolve(1); // 改变deferred对象的执行状态 触发done回调
//dtd.reject(); //跟resolve相反,触发fail回调
}, 1000);
return dtd;
}()).done(function(num) {
console.log(num);
}).done(function() {
console.log(2);
}).done(function() {
console.log(2);
})
//ajax默认就是返回deferred对象
$.when($.post("index.php", {
name: "wscat",
}), $.post("other.php"))
.done(function(data1, data2) {
//两个ajax成功才可以进入done回调
console.log(data1, data2);
}).fail(function(err) {
//其中一个ajax失败都会进入fail回调
console.log(err)
})
先处理微任务队列
再处理宏任务队列
微任务 | 宏任务 |
---|---|
then | setTimeout |
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);//定时器为宏任务
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {//then为微任务
console.log('promise2');
});
console.log('script end');
//先同步后异步
//先清空微任务再清空宏任务
输出的顺序是:script start, script end, promise1, promise2, setTimeout
console.log('script start');
setTimeout(function() {
console.log('timeout1');
}, 10);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
console.log('then1')
})
console.log('script end');
输出的顺序是:script start, promise1, script end, then1, timeout1, timeout2
安装一下依赖
npm i -D babel-core babel-polyfill babel-preset-es2015 babel-preset-stage-0 babel-loader
新建.babelrc
文件,输入以下内容
{
"presets": [
"stage-0",
"es2015"
]
}
新建一份index.js,把你的逻辑文件app.js
,后面require的任何模块都交给babel处理,polyfill支持await
和async
require("babel-core/register");
require("babel-polyfill");
require("./app.js");
参考Babel 6 regeneratorRuntime is not defined
//async定义里面的异步函数顺序执行
((async() => {
try {
//await相当于等待每个异步函数执行完成,然后继续下一个await函数
const a = await (() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve(2);
//reject(3)
}, 1000)
});
})();
const b = await (() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
//resolve(2);
reject(3)
}, 1000)
});
})();
console.log(4)
console.log(a) //3
return b;
} catch(err) {
//上面try中一旦触发reject则进入这个分支
console.log(err);
return err;
}
})()).then((data) => {
console.error(data)
}).catch((err) => {
console.error(err)
})
//分别输出213
注意点:
当然我个人觉得下面写法比较清晰点
//利用try...catch捕捉Promise的reject
async function ajax(data) {
try {
return await new Promise((resolve, reject) => {
setTimeout(() => {
console.log(data)
resolve(data); //成功
}, 2000);
});
} catch(err) {}
}
async function io() {
try {
const response = await new Promise((resolve, reject) => {
setTimeout(() => {
reject("io"); //失败
}, 1000);
});
//resolve执行才会执行下面这句return
return response
} catch(err) {
console.log(err);
}
}
//异步串行
(async() => {
await ajax("ajax1");
await ajax("ajax2");
await io();
})()
(async() => {
let [ajax1, ajax2] = await Promise.all([ajax("ajax1"), ajax("ajax2"),io()]);
return [ajax1,ajax2]
})()
使用new关键字初始化的之后是不存储在栈内存中的。为什么呢?new大家都知道,根据构造函数生成新实例,这个时候生成的是对象,而不是基本类型。再看一个例子
var a = new String('123')
var b = String('123')
var c = '123'
console.log(a==b, a===b, b==c, b===c, a==c, a===c)
>>> true false true true true false
console.log(typeof a)
>>> 'object'
我们可以看到new一个String,出来的是对象,而直接字面量赋值和工厂模式出来的都是字符串。但是根据我们上面的分析大小相对固定可预期的即便是对象也可以存储在栈内存的,比如null,为啥这个不是呢?再继续看
var a = new String('123')
var b = new String('123')
console.log(a==b, a===b)
>>> false false
很明显,如果a,b是存储在栈内存中的话,两者应该是明显相等的,就像null === null是true一样,但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致
说到这里,再去想一想我们常说的值类型和引用类型其实说的就是栈内存变量和堆内存变量,再想想值传递和引用传递、深拷贝和浅拷贝,都是围绕堆栈内存展开的,一个是处理值,一个是处理指针
一般来说栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了
垃圾回收方面,栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收
从一个向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终指向同一个对象。即复制的是栈中的地址而不是堆中的对象
从一个变量复向另一个变量复制基本类型的值,会创建这个值的副本