所谓"异步",简单说就是一个任务不是连续完成的,能够理解成该任务被人为分红两段,先执行第一段,而后转而执行其余任务,等作好了准备,再回过头执行第二段。前端
好比,有一个任务是读取文件进行处理,任务的第一段是向操做系统发出请求,要求读取文件。而后,程序执行其余任务,等到操做系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫作异步。ajax
相应地,连续的执行就叫作同步。因为是连续执行,不能插入其余任务,因此操做系统从硬盘读取文件的这段时间,程序只能干等着。后端
简单的说同步就是你们排队工做,异步就是你们同时工做。若是你还不太明白异步与同步,多看看JS基础的文章。api
CallBack意为“回调函数”,即异步操做执行完后触发执行的函数,例如:promise
$.get("http://api.xxxx.com/xxx",callback);
复制代码
当请求完成时就会触发callback函数。bash
callback能够完成异步操做,可是经历过JQuery时代的人应该都对某一种需求折磨过,举个例子:项目要求前端ajax请求后端接口列表类型名称,而后在用类型名称ajax请求列表id,在用id请求列表具体内容,最后代码大概是这样的框架
$.ajax({
url: "type",
data:1,
success: function (a) {
$.ajax({
url: "list",
data:a,
success: function (b) {
$.ajax({
url: "content",
data:b,
success: function (c) {
console.log(c)
}
})
}
})
}
})
复制代码
这是是单纯的嵌套代码,如若再加上业务代码,代码可读性可想而知,若是是开发起来还好,可是后期的维护和修改的难度足以让人疯掉。这就是那个JQuery时代的“回调地狱”问题。异步
为了解决“回调地狱”问题,提出了Promise对象,而且后来加入了ES6标准,Promise对象简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。async
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操做成功 */){
resolve(value);
} else {
reject(error);
}
});
复制代码
Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用本身部署。函数
resolve函数的做用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操做成功时调用,并将异步操做的结果,做为参数传递出去;reject函数的做用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操做失败时调用,并将异步操做报出的错误,做为参数传递出去。
Promise实例生成之后,能够用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
复制代码
then方法能够接受两个回调函数做为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不必定要提供。这两个函数都接受Promise对象传出的值做为参数。
这样采用 Promise,解决“回调地狱”问题,好比连续读取多个文件,写法以下。
var readFile = require('fs-readfile-promise');
readFile(fileA)
.then(function (data) {
console.log(data.toString());
})
.then(function () {
return readFile(fileB);
})
.then(function (data) {
console.log(data.toString());
})
.catch(function (err) {
console.log(err);
});
复制代码
可见这种写法要比CallBack写法直观的多。可是,有没有更好的写法呢?
Genrator 函数要用* 来比标识,yield关键字表示暂停。将函数分割出好多个部分,调用一次next就会继续向下执行。返回结果是一个迭代器,迭代器有一个next方法。
function* read() {
console.log(1);
let a = yield '123';
console.log(a);
let b = yield 9
console.log(b);
return b;
}
let it = read();
console.log(it.next('213')); // {value:'123',done:false}
console.log(it.next('100')); // {value:9,done:false}
console.log(it.next('200')); // {value:200,done:true}
console.log(it.next('200')); // {value:200,done:true}
复制代码
yield后面跟着的是value的值,yield等号前面的是咱们当前调用next传进来的值,而且第一次next传值是无效的。
处理异步的时候Generator和Promise搭配使用
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);//将readFile转为Promise对象的实例
function* r() {
let content1 = yield read('./2.promise/1.txt', 'utf8');
let content2 = yield read(content1, 'utf8');
return content2;
}
复制代码
这样看起来是咱们想要的样子,可是只写成这样还不行,想获得r()的结果还要对函数进行处理
function co(it) {
return new Promise(function (resolve, reject) {
function next(d) {
let { value, done } = it.next(d);
if (!done) {
value.then(function (data) { // 2,txt
next(data)
}, reject)
} else {
resolve(value);
}
}
next();
});
}
co(r()).then(function (data) {
console.log(data)//获得r()的执行结果
})
复制代码
这样的处理方式显然很麻烦,并非咱们想要,咱们想要直观的写起来就就像同步函数,并且简便的方式处理异步。有这样的方法吗?
ES2017 标准引入了 async 函数,使得异步操做变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
async function r(){
try{
let content1 = await read('./2.promise/100.txt','utf8');
let content2 = await read(content1,'utf8');
return content2;
}catch(e){ // 若是出错会catch
console.log('err',e)
}
}
复制代码
一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数返回的是promise
r().then(function(data){
console.log(data);
},function(err){
})
复制代码
至此,async-await函数已经能够咱们满意,之后会不会出现更优秀的方案?以咱们广大程序群体的创造力,相信必定会有的。
async-await函数其实只是Generator函数的语法糖,而Generator函数的实现方式也是要基于Promise,因此咱们队Promise的实现原理进行分析。
Promise对象有如下几种状态:
在上面了解了Promise的基本用法后,咱们先将Promise的框架搭起来
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的缘由
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
}
function reject(reason) { // 失败状态
}
try {
executor(resolve, reject)
} catch (e) { // 捕获的时候发生异常,就直接失败了
reject(e);
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
//then方法
})
复制代码
接下来当调用成功状态resolve的时候,会改变状态,执行回调函数:
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
复制代码
reject函数同理
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
复制代码
接下来咱们完成then函数
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
})
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
})
}
return promise2;
}
复制代码
Promise容许链式调用,因此要返回一个新的Promise对象promise2
Promise.prototype.then = function (onFulfilled, onRjected) {
//成功和失败默认不穿给一个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x多是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此时没有resolve 也没有reject
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
复制代码
在promise2内部定义一个变量x为回调函数的返回值,因为返回值可能会有多种可能的状况,因此咱们定义一个resolvePromise函数统一处理
返回值能够分为
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用了'))
}
// 断定x是否是一个promise,promise应该是一个对象
let called; // 表示是否调用过成功或者失败
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能仍是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 说明是一个普通值
resolve(x); // 调用成功回调
}
}
复制代码
若是返回值为对象或函数,且有then方法,那咱们就认为是一个promise对象,去调用这个promise进行递归,直到返回普通值调用成功回调。
最后,再加上一个catch方法,很简单
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
复制代码
这些就是promise的主要功能的原理,附上完整代码
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的缘由
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用了'))
}
let called;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2;
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
复制代码