zhengwei1949 / myblog

个人博客
10 stars 6 forks source link

也许,这是你需要的promise教程 #64

Open zhengwei1949 opened 7 years ago

zhengwei1949 commented 7 years ago

也许,这是你需要的promise教程

console.log('定时器开始');
setTimeout(()=>console.log('定时器进行中'));
console.log('定时器结束');

上面的代码的问题是,我们期待的是先打印'定时器开始',再打印定时器进行中,最后打印定时器结束,但是事实上却是'定时器开始'->'定时器结束'->'定时器结束'

为了解决这个问题,我们可以怎么办呢?来,我们把代码改造一下:

console.log('定时器开始');
setTimeout(()=>{
    console.log('定时器进行中');
    endTimer();
})
function endTimer(){
  console.log('定时器结束');
}

是的,我们必须要用回调函数

但是,这种方式会带来什么问题呢?

我们如果要在这整个全部执行完之后再来执行其他的代码怎么办?这时候,我们必须又搞一个回调函数:

function myTime(callback){
  console.log('定时器开始');
  setTimeout(()=>{
      console.log('定时器进行中');
      endTimer();
  })
  function endTimer(){
      console.log('定时器结束');
      callback();
  }
}

myTime(function(){
    console.log('定时器全部结束会执行这里的代码');
})

这时候,我们极端一些,假如我们把层次搞深一些:

function myTime(callback){
    console.log('定时器开始');
    setTimeout(()=>{
        console.log('定时器进行中');
        endTimer();
    })
    function endTimer(){
        console.log('定时器结束');
        callback();
    }
}

myTime(function(){
    myTime(function(){
        myTime(function(){
            myTime(function(){
                myTime(function(){
                    myTime(function(){

                    })
                })
            })
        })
    })
})

这时候我们会发现代码会变得极其的丑陋。由于在js当中,异步代码随处可见(事件、Node.js中的文件读写等操作、http操作、定时器等),所以,回调函数无处不在,如果回调的层次不够深,我们还可以接受,但是一旦回调的层次太深了,我们就会写出诸如如上的代码。

怎么办? --> 用promise,我们来看一下如果使用了promise之后代码长得像什么样子:

let myTime = function(){
    return new Promise((resolve,reject)=>{
        console.log('定时器开始');
        setTimeout(()=>{
            console.log('定时器进行中');
            resolve('定时器结束');
        })
    })
}

myTime()
.then((res)=>{
    console.log(res);
    return myTime();
})
.then((res)=>{
    console.log(res);
    return myTime();
})
.then((res)=>{
    console.log(res);
    return myTime();
})
.then((res)=>{
    console.log(res);
    return myTime();
})
.then((res)=>{
    console.log(res);
    return myTime();
})
.then((res)=>{
    console.log(res);
    return myTime();
});

perfect,完美有木有??

这里,我们可以直接上结论:

promise的好处:解决了异步代码嵌套层次过深的问题,能让我们的代码看起来更加的扁平化

改造ajax变成promise方式

function ajax(url) {
    // 直接返回一个Promise对象。
    return new Promise(function (resolve, reject) {
        var xmlRequest = new XMLHttpRequest();
        xmlRequest.onreadystatechange = function () {
            if (xmlRequest.readyState === XMLHttpRequest.DONE) {
                if (xmlRequest.status === 200) {
                    // 成功时,结果采用resolve回调返回。
                    resolve(xmlRequest.responseText);
                } else {
                    // 失败时,将出错信息采用reject回调返回。
                    reject(new Error("The response fail with code " + xmlRequest.status));
                }
            }
        };
        xmlRequest.open("get", url);
        xmlRequest.send(null);
    });
}

ajax("http://huiang.com").then(function(data){
    // data中包含返回的内容。
}).catch(function(err){
    // err包含错误异步调用的错误信息
});

但是其实我们没有必要自己改造,因为原生的H5已经为我们提供了一个fetch的api

fetch api

fetch('./a.json')
.then((res)=>{
    return res.json();
})
.then((data)=>{
    console.log(data)
})
.catch((e)=>{
    console.log('出错啦');
})

扩展 async/await

let myTime = function(){
    return new Promise((resolve,reject)=>{
        console.log('定时器开始');
        setTimeout(()=>{
            console.log('定时器进行中');
            resolve('定时器结束');
        })
    })
}

let fn = async function(){
    let ret;
    ret = await myTime();
    console.log(ret);
    ret = await myTime();
    console.log(ret);
    ret = await myTime();
    console.log(ret);
    ret = await myTime();
    console.log(ret);
}

fn()

参考资源

zhengwei1949 commented 7 years ago

todo:添加fetch api的catch方法以及json回调函数中的每个参数的意义解释

zhengwei1949 commented 6 years ago

要理解的是:promise也是异步的

zhengwei1949 commented 6 years ago

回调的理解: 你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。