参考文档

一、什么是promise

Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作

二、同步和异步

我们知道,JavaScript的执行环境是「单线程」。
所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程。
但实际上还有其他线程,如事件触发线程、ajax请求线程等。
这也就引发了同步和异步的问题

2-1 同步

同步模式,即单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。

  1. var x = true;
  2. while(x);
  3. console.log("don't carry out"); //不会执行

其中的while是一个死循环,它会阻塞进程,因此第三句console不会执行。

2-2 异步

异步模式,即与同步模式相反,可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。
最常见的异步模式就数定时器了

setTimeout(function() {
    console.log('taskA, asynchronous');
}, 0);
console.log('taskB, synchronize');

//输出
taskB, synchronize
taskA, asynchronous

2-3 回调函数

  1. 上例中,setTimeout里的function便是回调函数。可以简单理解为:(执行完)回(来)调(用)的函数
  2. 可以看出,回调函数是一段可执行的代码段,它以「参数」的形式传递给其他代码,在其合适的时间执行这段(回调函数)的代码。
  3. 也就是说,回调函数不仅可以用于异步调用,一般同步的场景也可以用回调。在同步调用下,回调函数一般是最后执行的。而异步调用下,可能一段时间后执行或不执行(未达到执行的条件)。


三、为什么使用Promise

解决回调地狱,将异步的代码以同步的方式显示出来,简化代码,方便阅读
使用Promise,我们就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。

//使用Promise之前的回调地狱
request('test1.html', '', function(data1) {
    console.log('第一次请求成功, 这是返回的数据:', data1);
    request('test2.html', data1, function (data2) {
        console.log('第二次请求成功, 这是返回的数据:', data2);
        request('test3.html', data2, function (data3) {
            console.log('第三次请求成功, 这是返回的数据:', data3);
            //request... 继续请求
        }, function(error3) {
            console.log('第三次请求失败, 这是失败信息:', error3);
        });
    }, function(error2) {
        console.log('第二次请求失败, 这是失败信息:', error2);
    });
}, function(error1) {
    console.log('第一次请求失败, 这是失败信息:', error1);
});
//使用Promise后
sendRequest('test1.html', '').then(function(data1) {
    console.log('第一次请求成功, 这是返回的数据:', data1);
    return sendRequest('test2.html', data1);
}).then(function(data2) {
    console.log('第二次请求成功, 这是返回的数据:', data2);
    return sendRequest('test3.html', data2);
}).then(function(data3) {
    console.log('第三次请求成功, 这是返回的数据:', data3);
}).catch(function(error) {
    //用catch捕捉前面的错误
    console.log('sorry, 请求失败了, 这是失败信息:', error);
});

四、Promise的基本用法

4-1 基本用法

Promise对象代表一个未完成、但预计将来会完成的操作。
它有以下三种状态:

  • pending:初始值,不是fulfilled,也不是rejected
  • fulfilled:代表操作成功
  • rejected:代表操作失败

Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用

//构建Promise
var promise = new Promise(function (resolve, reject) {
    if (/* 异步操作成功 */) {
        resolve(data);
    } else {
        /* 异步操作失败 */
        reject(error);
    }
});

类似构建对象,我们使用new来构建一个Promise。Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是就是「回调函数」,由JavaScript引擎提供

resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

4-2 基本API

.then()

语法:Promise.prototype.then(onFulfilled, onRejected)

.catch()

语法:Promise.prototype.catch(onRejected)

该方法是.then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。

promise.then(function(data) {
    console.log('success');
}).catch(function(error) {
    console.log('error', error);
});

/*******等同于*******/
promise.then(function(data) {
    console.log('success');
}).then(undefined, function(error) {
    console.log('error', error);
});
var promise = new Promise(function (resolve, reject) {
    throw new Error('test');
});
/*******等同于*******/
var promise = new Promise(function (resolve, reject) {
    reject(new Error('test'));
});

//用catch捕获
promise.catch(function (error) {
    console.log(error);
});
-------output-------
Error: test

五、新版ajax本身就是一个Promise

封装ajax
     var baseUrl="https://music.aityp.com/"
     function http(url){
      return new Promise((resolve,reject)=>{
       $.ajax({
           url:baseUrl+url,
           type:"get",
           success:res=>{
               resolve(res)
           },
           error:err=>{
               reject(err)
           }
       })
   })
}

  //调用
        <script>
          http("top/playlist?cat=华语").then(res=>{
              let id=res.playlists[0].id;
              return id
          }).then(res=>{
              http(`playlist/detail?id=${id}`).then(res=>{
                  let id=res.playlists.trackIds[0].id;
                  return id
          }).then(res=>{
              http(`song/url?id=${res}`).then(res=>{
                  console.log(res)
              })
          })
        })
    </script>

六、async/await

参考文档

6-1 关于async

async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。

6-2 async特点

  • async/await更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,可以认为是async wait的简写, 用于等待一个异步方法执行完成;
  • async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
  • 可以通过多层 async function 的同步写法代替传统的callback嵌套


6-3 async语法

如果函数前面加上async关键字,函数就会变成一个promise 可以通过then触发

async function go(){
           return 1;
       }
       /* then去触发执行 */
       go().then(res=>{
           console.log(res)   //1
       })

6-4 async关键字

await后面一定要跟promise,执行一个promise
await一定是在一个async里面 只在async里面有效

      async function go(){
           return 1;
       }
       async function getRes(){
           var data=await go();
           console.log(data)    //1
       }
       getRes()

await关键字可以获取promise的结果

   async function getCat(){
           return "华语"
       }
       async function detail(){
           return "detail"
       }
       async function url(){
           return "url"
       }
       async function getRes(){
           var cat=await getCat();
           console.log(cat)     //"华语"
           var de=await detail();
           console.log(de)     //"detail"
           var ur=await url();    //"url"
           console.log(ur)
       }
       getRes()

6-5 async嵌套

用forEach遍历数组,不能把异步的变为同步,在遍历前面加await,使变为同步
await就是跟在他后面的promise执行完毕之后,才会执行下面的代码

<script>
        async function go(){
            return "go"
        }    
        var arr=['html','css','js','vue']
        async function test(){
            console.log(1);
          await arr.forEach(async item=>{
                var data=await go();
                console.log(data)
            })
            console.log(2)               //输出顺序:1 go() 2
        }
        test()
        /* console.log(1);
        arr.forEach(item=>{
            console.log(item)
        })
        console.log(2) */
    </script>

七、手写一个Promise

<script>
    //Promise有几种状态?  pending  fulfilled  rejected
    //Promise有哪些方法?  resolve  reject
    //Promise有哪些属性?  value   reason
    //Promise有一个then方法
    class Promise {
        constructor(fn) {
            this.state = "pending";
            this.value = null;
            this.reason = null;
            this.then = function (onFulfilled, onRejected) {
                //执行then方法的时候,需要查看Promise的状态,如果Promise执行成功,需要执行成功的的回调;如果Promise失败,需要执行Promise失败的回调
                switch (this.state) {
                    case 'fulfilled':
                        onFulfilled(this.value);
                        break;
                    case 'reject':
                        onRejected(this.reason);
                        break;
                }
            };

            let resolve = value => {
                if (this.state === 'pending') {
                    this.state = "fulfilled";
                    this.value = value;
                }
            }

            let reject = reason => {
                if (this.state === 'pending') {
                    this.state = "reject";
                    this.reason = reason;
                }
            }

            //执行fn函数
            try {
                fn(resolve, reject)
            } catch (e) {
                reject(e)
            }
        }
    }

    var p = new Promise(function (resolve, reject) {
        console.log("22222")
        resolve("小明");
    })

    //Promise.then方法接收两个函数做为参数
    //第一个函数参数是Promise成功的回调
    //第二个函数参数是Promise失败的回调
    p.then((msg) => {
        console.log(msg,"AAAAAAAAAA")
    }, (err) => {
        console.log(err,"BBBBBBBBBBBBBBB");
    })
</script>

八、Promise对象是什么

Promise对象是ES6( ECMAScript 2015 )对于异步编程提供的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
Promise本身不是异步的,只不过Promise中可以有异步任务,new Promise()的第一个函数参数是立马执行的。

function func1(a){
    return new Promise((resolve,reject) => {
        if(a > 10){
            resolve(a)
        }else{
            reject(b)
        }
    })
};

func1('11').then(res => {
    console.log('success');
}).catch(err => {
    console.log('error');
})


//Promise构造函数接受一个函数作为参数,该函数的两个参数分别resolve 和 reject。它们是两个函数,由
JavaScript 引擎提供。
1.resolve函数的作用是: 将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 fulfilled),
在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

2.reject函数的作用是: 将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),
在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

//Promise对象实例的方法,then 和 catch:
1  .then方法: 用于指定调用成功时的回调函数。
    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例),因此可以采用链式写法,
  即then方法后面再调用另一个then方法。

2  .catch方法: 用于指定发生错误时的回调函数。