【前端入门笔记15】异步与Promise

361 阅读4分钟

文章回答以下问题:

  • 什么是异步,什么是同步
  • 回调跟异步的关系
  • 如何知道一个函数是异步
  • promise是什么,为什么需要他
  • promise的初级用法

什么是异步?什么是同步?

程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。

如果能直接拿到结果,就是同步,比如排队买东西,必须人在那里,人拿到结果才走。同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

如果不能直接拿到结果,就是异步,比如拿号排队,排到之前可以干其他事情。你可以每十分钟去询问一下(轮询),也可以扫码微信接收通知,快到时间微信会通知你(回调)。异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。

什么是回调?

写给自己用的函数,不是回调,写给别人用的函数,才是回调。比如request.onreadystatechange函数是给浏览器回头调用的函数(当结束时,如果状态码是4...就执行...),所以他就是一个回调。

回调举例👇

function f1(){}
function f2(fn){}
	fn()
}
f2(f1)

//我没有调用f1,我把f1传给了f2,f2调用了f1,f1是我写给f2调用的函数,所以f1是回调,f2不是。

异步和回调的关系

联系

回调是实现异步的一种方式。异步任务需要在得到结果时通知JS来拿结果,如何通知呢?可以让JS留一个函数地址(电话号码)给浏览器,异步任务完成时,浏览器调用该函数地址即可(拨打电话),同时把结果作为参数传给该函数(电话里说可以吃了),这个函数就是我写给浏览器调用的,所以是回调函数。

区别

异步任务需要用到回调函数来通知结果,同时也可以用轮询来通知结果,不是一定要用回调。 回调不一定只在异步里用到,也可以在同步任务里用,比如 array.forEach(n ⇒ console.log(n) 就是同步回调。

如何知道一个函数是同步还是异步

如果一个函数的返回值处于

  • setTimeout
  • AJAX(即XMLHttpRequest)
  • AddEventListener 事件监听

在这三个内部时,那么这个函数就是异步函数

当异步任务有两种结果时

常见方法之一就是搞两个回调,对应成功和失败,比如在AJAX中,可能是这样:

ajax = (method, url, options) => {
    const success = options.success
    const fail = options.fail
    const request = new XMLHttpRequest()
    request.open(method, url)
    request.onreadystatechange = () =>{
        if(request.readyState === 4){
            if(request.status < 400){
                success.call(null, request.response) //成功
            }else if(request.status >= 400){
                fail.call(null, request, request.status)  //失败
            }
        }
    }
    request.send()
}

ajax('get','/xxx',{
    success(response){}, fail:(request, status)=>{}
})

//如此封装是有问题的,这里只是举例说明成功和失败的情况

上面这种方法的不足:

  • 不规范。名称五花八门,可以是success+error,也可以是success+fail,甚至是done+fail
  • 容易出现回调地狱
  • 很难进行错误处理

回调地狱举例:

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // 中间可能有20层
      });
    });
  });
});

所以出现了promise,来解决以上三个问题

Promise

废话不多说,我们看用了promise之后,之前的例子可以改成:

ajax = (method, url, options) => {
	return new Promise((resolve, reject)=>{  //解决回调地狱的精髓,同时统一名称
    const success = options.success
    const fail = options.fail
    const request = new XMLHttpRequest()
    request.open(method, url)
    request.onreadystatechange = () =>{
        if(request.readyState === 4){
            if(request.status < 400){
                resovle.call(null, request.response)
            }else if(request.status >= 400){
                reject.call(null, request, request.status)
            }
        }
    }
    request.send()
	})
}

ajax('get','/xxx').then((response)=>{}, (request, status)=>{})

如此,便解决了一开始说的三个问题

promise初级用法总结

  1. return new Promise((resolve, reject)=>{...}
  2. 任务成功则调用resolve(result)
  3. 任务失败则调用reject(error)
  4. resolve 和 reject 再去调用成功和失败函数
  5. 使用.then(success, fail)传入成功和失败函数