promise总结(main)

351 阅读5分钟

1. promise概念

promise每次调用都是异步的,代表一个异步操作。有三种状态:

  1. pending(进行中)、
  2. fulfilled(已成功)
  3. rejected(已失败)

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

  1. promise的缺点:
    (1) 无法取消promise,一旦新建它会立即执行,无法中途取消
    (2) 如果不设置回调函数,promise内部抛出的错误,不会反应到外部
    (3) promise的状态一旦改变,就不会再变,且无法返回之前的状态
    (4) 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

  2. promise的优点:
    (1) 对象的状态不受外界影响。
    (2) 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
    (3) 将异步操作以同步操作的流程表达出来,避免了层层嵌套回调函数。
    (4) 提供统一的接口,使得控制异步操作更加容易。

  3. 错误处理尽量在catch中执行,因为可以将错误统一处理。强烈建议在所有队列最后都加上 .catch(),以免漏掉错误处理造成意想不到的问题

  • promise.then(onFulfilled, onRejected),resolve(成功)时 onFulfilled 会被调用,reject(失败)时 onRejected 会被调用

  • Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

  • 使用promise.then(onFulfilled, onRejected) 的话在 onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的。因此建议,不要将错误处理写在then中的第二个函数内,而是在结尾写在.catah()内进行统一错误处理

2. promise的使用 由浅到深

  • 最基础用法
let promise = new Promise((resolve, reject) => {
  resolve("this is  information");
  reject("这是错误消息");
});

promise.then(res => {
  console.log(res);
})
.catch(err => {
  console.log(err);
});
  • 也可以这样使用
let promise = getAsyncPromise("fileA.txt");
    promise.then(function(result) {
// 获取文件内容成功时的处理
}).catch(function(error) {
    // 获取文件内容失败时的处理
});
  • 这种使用方式也比较普遍。在then方法里,写两个函数也能实现then和catch的作用。第二个函数代表catch
function asyncFunction() {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      resolve("Async Hello World!");
    }, 2000);
  });
}

asyncFunction()
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

asyncFunction().then(
  function (value) {
    console.log(value);
  },
  function (error) {
    console.log(error);
  }
);

3. 将get请求封装成一个promise

  • 注意状态码这块,不能直接判断200 && 4 ,因为状态码会由0-4。 按照下列方法进行错误处理才是合理的

     function getUrl(url) {
      let promise = new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.onload = function() {
          if (xhr.status === 200) {
            resolve(xhr.responseText);
          } else {
            reject(new Error(xhr.statusText));
          }
        };
        xhr.onerror = function() {
          reject(new Error(xhr.statusText));
        };
        xhr.open("get", url, true);
        xhr.send();
      });
      return promise;
     }
      let url = "http://httpbin.org/get";
      getUrl(url).then(res => {
        console.log(res);
      })
      .catch(err => {
        console.error(err);
      });
    

4. promise的简写方式

  • Promise.resolve() 是new promise(resolve=>{})的语法糖
  • Promise.reject( new Error("出错了") )
  • Promise.all()
  • Promise.race()
function getURL(URL) {
  return new Promise(function (resolve, reject) {
    var req = new XMLHttpRequest();
    req.open("GET", URL, true);
    req.onload = function () {
      if (req.status === 200) {
        resolve(req.responseText);
      } else {
        reject(new Error(req.statusText));
      }
    };
    req.onerror = function () {
      reject(new Error(req.statusText));
    };
    req.send();
  });
}
let request = {
  comment: function getComment() {
    return getURL("http://azu.github.io/promises-book/json/comment.json").then(
      JSON.parse
    );
  },
  people: function getPeople() {
    return getURL("http://azu.github.io/promises-book/json/people.json").then(
      JSON.parse
    );
  }
};
function main() {
  return Promise.all([request.comment(), request.people()]);
}
// 运行示例
main().then(function (value) {
    console.log(value);
}).catch(function(error){
    console.log(error);
});

5. Node的微任务(microtask)和宏任务(macrotask)

记住以下规则:

  1. 事件循环中每一个tick执行的顺序是, 先执行同步任务(main script), 然后执行微任务(microtack), 最后执行宏任务(macrotask)。 可以自己画张图, 将不同的任务放在不同的区域, 最后依次执行。
  2. 存在微任务的话,需要执行完所有的微任务, 再执行下一个宏任务
  3. await后面的代码会被作为一个promise返回的微任务放入到微任务队列中
  4. nextTick是微任务, 执行顺序先于promise
  5. process是微任务,的执行顺序是先于promise的
  6. 在使用 await 的时候我们暂停了函数,而非整段代码

2)、微任务都执行完之后,执行下一个宏任务

  • 从一些事件循环的Tick来说。 Node的事件循环更复杂, 它也分为微任务和宏任务
    1. 微任务(microtask): promise的then回调、 promise.nextTick、 queueMicrotask
    2. 宏任务(macrotask): setTimeout、 setInterval、 IO事件、 setImmediate、 close事件
  • Node中的事件循环不只是 微任务队列和宏任务队列(以下队列执行顺序从上到下)
  1. 微任务队列:

✅ next tick queue: process.nextTick
✅ other queue: Promise的then回调、 queueMicrotask

  1. 宏任务队列:

✅ timer queue: setTimeout setInterval
✅ poll queue: IO事件
✅ check queue: setImmediate
✅ close queue: close事件

6. 几道经典的异步笔试题

记住这些原则, 基本上面试题就不会有问题

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function () {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
    console.log('promise1');
    resolve();
}).then(function () {
    console.log('promise2');
});
console.log('script end');
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout0')
}, 0)

setTimeout(function () {
  console.log('setTimeout2')
}, 300)

setImmediate(() => console.log('setImmediate'));

process.nextTick(() => console.log('nextTick1'));

async1();

process.nextTick(() => console.log('nextTick2'));

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
  console.log('promise2')
}).then(function () {
  console.log('promise3')
})

console.log('script end')
setTimeout(() => {
   console.log('settimeout');
},0);

console.log('t1');

fetch('http://dict.qq.com').then(function(response){
  return response.json()
}).then(function(myJson){
  console.log(myJson);
}).catch(function(err){
  console.log(err);
})

console.log('ferch zhihou');

async function async1(){
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async1()
console.log('t2');

new Promise((resolve)=>{
  console.log('promise');
  resolve()
}).then(()=>{
  console.log('promise.then');
})

console.log('t3');
async function async2(){
  console.log('async2');
}
console.log('t4');

7. async和await

  • Async(异步) 函数或多或少允许你编写顺序的 JavaScript 代码,而无需将所有逻辑包装在 callbacks(回调),generators(生成器) 或 promises 中
  • async 和 await 关键字基于 pormisegenerator 做了简单的封装。本质上,它允许我们在所需的任意位置使用 await 关键字来“暂停”一个函数。

8. 参考链接

搞懂异步

promise进阶篇

javascript promise迷你书

使用 ES2017 中的 Async(异步) 函数 和 Await(等待)