[JavaScript] Promise 与 Ajax/Axios

updateTime: 2019-4-5 23:00 javascript

updateContent: async在项目中的使用简谈vue

前言

在单线程的js执行中,必然须要异步的出现来协程操做,不然就不用玩了, 而在js异步编程方案中,咱们经历了回调地狱后终于推出了更合理强大的新方案,那就是——Promise,而在经历了co模块的洗礼后,es7顺利推出了Generator 的语法糖——Async(谁用谁知道,真滴爽=-=)java

Promise 承诺

What

用于处理异步回调的一种解决方案,比传统回调更强大合理

WhyThisName

根据promise对象的第一特征,除了异步操做能够影响其状态,其余外界操做都不能影响,表示一种承诺,即其余手段没法改变。ios

两个特征:

1. 对象状态不受外界影响,三种状态为: pengding, fulfilled(已成功), rejected(已失败)
2. 状态改变后不在改变,只有两种改变: pending->fulfilled / pending->rejected

三个缺点: 

1. new后没法取消
2. 无回调则内部错误没法抛出

3. 当处于pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)
es6

How to use 

const promise = new Promise((resolve, reject) => {
  if (异步操做完毕) {        
      resolve(val)      
  } else {       
      reject(error)      
  }
 })  
 promise.then((val) => { 
     // doSuccess 
 }, (err) => {      // doFailure })复制代码


基于Promise的Ajax,告别回调地狱

手打一时爽..ajax

// 手写一个ajax by promise 格式崩溃了见谅..
function _ajaxGet (url, params) {
      const promise =  new Promise(function(resolve, reject){
         const handle = function () {
             if (this.readyState == 4) {
                 if (this.status === 200 || this.status === 304) {
                     resolve(this.response)                                                             
                 } else {
                    reject(new Error(this.statusText))                                          
                 }                
             }                            
         }            
         const XHR = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP')
         params = params || ''
         XHR.open('GET', url + params, true)
         XHR.onreadystatechange = handle
         XHR.responseType = 'json'
         XHR.setRequestHeader('Accept', 'application/json')
         XHR.send()        
     })        
     return promise
}    
function _ajaxPost (url, data) {
        const promise =  new Promise(function(resolve, reject){
            const handle = function () {
                if (this.readyState == 4) {
                    if (this.status === 200 || this.status === 304) {
                        resolve(this.response)
                                                                  
} else {
                        
reject(new Error(this.statusText))
                                            
}                
}                            
}            
const XHR = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP')
XHR.open('POST', url, true)            
XHR.onreadystatechange = handle            
XHR.responseType = 'json'            
XHR.setRequestHeader('Content-Type', '"application/json')            
XHR.send(data)                    
})        
return promise    
}    
_ajaxGet('test.json').then(res => {
        console.log(res)
}).catch(err => {
        console.log(err)
})    
_ajaxPost('test.json', {id: 123})复制代码


其实把上面的代码封装封装加一些其余的配置参数,就是一个简单的axios了(?我本身觉得的),不过axios的原理就是经过promise实现的就没错了。编程

能够发现区别于传统的回调里面写异步,回调一层又一层,真的要好不少。json

快速认识Promise对象的一些方法

1. Promise.all([p1,p2,p3])axios

相似于串联电路,数组

数组中的全部promise实例状态都为已定型(resolve)时,则返回一个全部实例的返回值数组进行该promise以后的回调,

如有一个/多个为reject则返回第一个reject的promise的error

demo(来自es6阮老师书中demo)

// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});复制代码

2. Promise.race([p1,p2,p3])

相似于并联电路,

返回多个实例中第一个进行了状态变换的实例的返回值做为回调函数的参数

3. 后续补充...

Promise内部一探

虽然没看源码,但猜想是用观察者模式实现的,经过改变状态值来触发对应回调函数(根据其特性加一些别的参数设置),后面出一期观察者模式与promise。

一块儿来实现一个Promise

Promise的缺陷

假设有这样一个场景,五个动画,顺序加载到一个dom上,要求五秒内完成,不然提示报错并关闭动画。好了这个场景先不去实现,可想而知,为了实现第一个顺序加载,你须要

.then(res => {//anim1(); resolve()})

.then(res => {//anim2(); resolve()})

.then(res => {//anim3(); resolve()})

.then(res => {//anim4(); resolve()})

.then(res => {//anim5()})

代码越写越长,横纵变胖,贼难看,而且不容易察看

这个时候

Async Await(Generaor yiled)闪亮登场

今天先写到这。。。累,明天接着写

下面是目录

Generator 与 执行器 与 Thunk(携程函数)

Generator函数 function* helloworld () {}  利用Generator能够实现js的状态机

是否是很像c中的指针, 没错Generator函数调用后不会执行(交出函数执行权),可是会返回一个遍历器对象,经过对该对象的next(继续)方法调用,对函数内代码进行执行,每次next执行会在遇到的第一个yield(暂停)停下,而后返回一个键值分别为value,done的对象来表示当前暂停右侧表达式的值和遍历的状态。在多任务状况下,多个Generator(async:我是谁我在那)经过yield(await:我是谁我在哪,而且我遇到的不是异步我会当即执行再返回一个已定型的promise对象)来交换控制权。

一直执行继续(next)而后当遍历状态(done)为true时遍历结束,下次再执行就会一直返回

{value: undefined, done: true}

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }
复制代码

代码终于看起来比回调清晰了,可是流程却有些不如意,那么如何自动执行呢,有请

Thunk 传名调用的一种实现策略

what: 计算机语言求值策略中的传名调用,即将表达式直接传入,减小没必要要计算损失。编译器的“传名调用”实现,每每是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫作 Thunk 函数。

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName) {
  return function (callback) {
    return fs.readFile(fileName, callback);
  };
};

var readFileThunk = Thunk(fileName); 复制代码

readFileThunk(callback); 

固然js是值传递调用!在js中Thunk起到的主要做用是多参变单参后将回调传回。上面看到将callback传回。

而在这里对Genrator的自动流程管理实现的帮助中Thunk起到的是对next指针传出后再传入上次yield表达式执行完毕地址,这样就能够能够把执行权在next传出后又从新传入,拿回执行权。

一句话,上次异步成功后自动继续yield后代码。

可是看起来好麻烦,还不如写写thenthenthen(Promise自动执行,变胖就变胖,口亨)是吧,还要写Genrator的执行器。这个时候Co闪亮登场。

Co 小轻美

var co = require('co');
co(gen);
// Generator函数传入co中自动执行复制代码

what: Thunk与Promise的结合糖

tip: 注意了co的实参只能是Thunk 函数或 Promise 对象。支持并发。

Async Await的优势

  • 内置执行器
  • 更好的语义
  • 更广的适用性
  • 返回值是 Promise

实例Demo与项目中应用简谈

最近使用vue作了一个项目相似于阿里的iconFont,首先用一个很简单的首页状态逻辑来介绍async的使用(顺序加载)

1. 当进入首页组件前在app组件created中首先调用initUserInfo异步函数,该函数在store的index.js的dispatch中定义

2. 而后咱们须要在首页组件加载时调用几个请求来渲染对应用户的数据,这几个请求须要使用userid这个数据

若是用promise来实现的话,那就是如下代码

methods: {
initUserInfo () {
    return _axios.get(uri).then(res => { // 参考上面用promise实现的ajax.getJson
        commit('userInfo', res.data.data) // 全局状态更新处理 
    })
}
initMyproInfo () {
    // ...
    return _axios.post(getProUri, {usrId: this.userInfo.id}) // ...mapGetters(['usrInfo'])
}
,
created () {
     this.initUserInfo().then(res => {
        console.log(res) // 打印获取到的用户数据
        return this.initMyproInfo()
    }).then(res => { // 这个回调依赖于调用他的promise对象状态
        this.this._message.info(`data id is ${data.id}`)
    }).catch(err => { // 捕捉then中第一个错误
        console.log(err)
    }) 
}
复制代码

若是用async 来实现的话

//...mapActions(['initUserInfo'])
async initUserInfo ({commit}) {
    await let {data} = _axios.get(uri)
    commit('initUserInfo', data.data) // 全局状态更新处理
    return data.data            
}
async created () { // 是否是看起来清爽多了!
    await this.initUserInfo()
    let data = await this.initMyproInfo() // 正常结果下,await后是一个promise对象,返回该对象的结果,在本例中即返回pro请求后的结果res
    this._message.info(`data id is ${data.id}`)
}复制代码




Last, 各位若是以为还喜欢,对你理解有用,麻烦点个赞吧(给您鼓掌了!^_^)

若是有错误的地方,请您不吝指出,谢谢!

相关文章
相关标签/搜索