所谓"异步",简单说就是一个任务不是连续完成的,能够理解成该任务被人为分红两段,先执行第一段,而后转而执行其余任务,等作好了准备,再回过头执行第二段。前端
好比,有一个任务是读取文件进行处理,任务的第一段是向操做系统发出请求,要求读取文件。而后,程序执行其余任务,等到操做系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫作异步。ajax
相应地,连续的执行就叫作同步。因为是连续执行,不能插入其余任务,因此操做系统从硬盘读取文件的这段时间,程序只能干等着。后端
简单的说同步就是你们排队工做,异步就是你们同时工做。若是你还不太明白异步与同步,多看看JS基础的文章。api
CallBack意为“回调函数”,即异步操做执行完后触发执行的函数,例如:promise
$.get("http://api.xxxx.com/xxx",callback);
当请求完成时就会触发callback函数。框架
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时代的“回调地狱”问题。async
为了解决“回调地狱”问题,提出了Promise对象,而且后来加入了ES6标准,Promise对象简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。函数
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操做成功 */){ resolve(value); } else { reject(error); } });
Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用本身部署。ui
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) }