首頁 > web前端 > js教程 > Promise是什麼? Promise的介紹

Promise是什麼? Promise的介紹

不言
發布: 2018-10-17 14:55:13
轉載
3594 人瀏覽過

這篇文章帶給大家的內容是關於 Promise是什麼? Promise的介紹,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

其實想寫 Promise 的使用已經很久了。一個是在實際編碼的過程中常用到,一個是確實有時候小夥伴們在使用時也會遇到一些問題。

Promise 也確實是 ES6 中 對於寫 JS 的方式,有著真正最大影響的 API 特性之一。
本文是實際使用使用過程中的一個總結
看看檔案建立時間 2017-10-09,拖延症真是太可怕了。 。 。還是得增強執行力啊!不忘初心,加油!

前言&& 基礎概念

Promise 是解決JS 非同步的方案,相較於傳統的回呼函數,Promise 能解決多個回調嚴重嵌套的問題。

Promise 物件代表一個非同步操作,有三種狀態: pending、fulfilled 或 rejected ,狀態的轉換只能是 pending -> fulfilled 或 pending -> rejected ,且這個過程一旦發生就不可逆轉。

個人認為講解Promise 實際上需要分成兩個部分

  1. 對於Promise 建構子的使用說明。

  2. Promise 原型物件上的一些方法。

Promise 建構子

ES6 規定,Promise 物件是建構函數,用來產生 Promise 實例。

Promise 建構函式接受一個函式作為參數,函式的兩個參數分別是 resolve 和 reject 。它們是兩個函數,由 JavaScript 引擎提供,不用自己部署。

resolve 函數的作用是將Promise 物件的狀態從「未完成」變為「成功」(即從pending 變為fulfilled ),在非同步操作成功時調用,並將非同步操作的結果,作為參數傳遞出去;
reject 函數的作用是,將Promise 物件的狀態從「未完成」變為「失敗」(即從pending 變為rejected ),在非同步操作失敗時調用,並將非同步操作報出的錯誤,作為參數傳遞出去。

下面程式碼創造了一個 Promise 實例。

function request() {
  return new Promise((resolve, reject) => {
    /* 异步操作成功 */
    setTimeout(() => {
      resolve("success");
    }, 1000);
    // 取消注释这里可以体现,Promise 的状态一旦变更就不会再变化的特性
    // reject('error');
  });
}
登入後複製

接收

request()
  .then(result => {
    console.info(result);
  })
  .catch(error => {
    console.info(error);
  });
登入後複製

上述new Promise() 之後,除去用catch 去捕獲錯誤之外,也可以用then 方法指定resolvereject 的回呼函數
也能達到捕獲錯誤的目的。

request().then(
  result => {
    console.info(result);
  },
  error => {
    console.info(error);
  }
);
登入後複製

原型上的方法

Promise.prototype.then()

p.then(onFulfilled, onRejected)
登入後複製

then 方法是定義在Promise.prototype 上的方法,如上面的例子一樣,有兩個參數,fulfilled 的回呼函數和rejected 的回呼函數,第二個參數時可選的。

兩個關鍵點:

  1. then 方法的回傳值是一個新的Promise 實例,所以對於呼叫者而言,拿到一個Promise 對象,呼叫then 後仍然會傳回一個Promise ,而它的行為與then 中的回呼函數的回傳值有關。如下:

  • 如果then 中的回呼函數傳回一個值,那麼then 傳回的Promise 將會成為接受狀態,並且將傳回的值作為接受狀態的回調函數的參數值。

  • 如果 then 中的回呼函數拋出錯誤,那麼 then 傳回的 Promise 將會成為拒絕狀態,並且將拋出的錯誤作為拒絕狀態的回呼函數的參數值。

  • 如果then 中的回呼函數傳回一個已經是接受狀態的Promise,那麼then 傳回的Promise 也會變成接受狀態,並且將那個Promise 的接受狀態的回呼函數的參數值作為該被傳回的Promise 的接受狀態回呼函數的參數值。

  • 如果then 中的回呼函數傳回一個已經是拒絕狀態的Promise,那麼then 傳回的Promise 也會變成拒絕狀態,並且將那個Promise 的拒絕狀態的回呼函數的參數值作為該被傳回的Promise 的拒絕狀態回呼函數的參數值。

  • 如果then 中的回呼函數回傳一個未定狀態(pending)的Promise,那麼then 回傳Promise 的狀態也是未定的,並且它的終態與那個Promise 的終態相同;同時,它變成終態時呼叫的回呼函數參數與那個Promise 變成終態時的回呼函數的參數是相同的。

  1. 鍊式呼叫。把嵌套回呼的程式碼格式轉換成一種鍊式呼叫的縱向模式。

比如說回呼形式: 一個回呼地獄的例子

a(a1 => {
  b(a1, b1 => {
    c(b1, c1 => {
      d(c1, d1 => {
        console.log(d1);
      });
    });
  });
});
登入後複製

這樣的橫向擴展可以修改成(a,b,c,d)均為返回Promise 的函數

a()
  .then(b)
  .then(c)
  .then(d)
  .then(d1 => {
    console.log(d1);
  });
//===== 可能上面的例子并不太好看 ===下面这样更直观
a()
  .then(a1 => b(a1))
  .then(b1 => c(b1))
  .then(c1 => d(c1))
  .then(d1 => {
    console.log(d1);
  });
登入後複製

這樣的縱向結構,看起來清爽多了。

Promise.prototype.catch()

除了 then() ,在 Promise.prototype 原型鏈上的還有 catch() 方法,這個是拒絕的情況的處理函數。

其实 它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部 calls obj.then(undefined, onRejected)).

// 1.
request().then(
  result => {
    console.info(result);
  },
  error => {
    console.info(error);
  }
);

// 2.
request()
  .then(result => {
    console.info(result);
  })
  .catch(error => {
    console.info(error);
  });
登入後複製

如上这个例子:两种方式在使用,与结果基本上是等价的,但是 仍然推荐第二种写法,下面我会给出原因:

  1. 在 Promise 链中 Promise.prototype.then(undefined, onRejected),onRejected 方法无法捕获当前 Promise 抛出的错误,而后续的 .catch 可以捕获之前的错误。

  2. 代码冗余

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("reject");
  }, 1000);
})
  .then(
    result => {
      console.log(result + "1");
      throw Error(result + "1"); // 抛出一个错误
    },
    error => {
      console.log(error + ":1"); // 不会走到这里
    }
  )
  .then(
    result => {
      console.log(result + "2");
      return Promise.resolve(result + "2");
    },
    error => {
      console.log(error + ":2");
    }
  );
// reject1, Error: reject1:2
登入後複製

如果使用 .catch 方法,代码会简化很多,这样实际上是延长了 Promise 链

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("reject");
  }, 1000);
})
  .then(result => {
    console.log(result + "1");
    throw Error(result + "1"); // 抛出一个错误
  })
  .then(result => {
    console.log(result + "2");
    return Promise.resolve(result + "2");
  })
  .catch(err => {
    console.log(err);
  });
// reject1, Error: reject1:2
登入後複製

Promise.prototype.finally()

暂未完全成为标准的一部分,处于:Stage 4

finally() 方法返回一个 Promise,在执行 then() 和 catch() 后,都会执行finally指定的回调函数。(回调函数中无参数,仅仅代表 Promise 的已经结束

等同于使用 .then + .catch 延长了原有的 Promise 链的效果,避免同样的语句需要在 then() 和 catch() 中各写一次的情况。

mdn-Promise-finally

Promise 对象上的方法

Promise.all() 用来处理 Promise 的并发

Promise.all 会将多个 Promise 实例封装成一个新的 Promise 实例,新的 promise 的状态取决于多个 Promise 实例的状态,只有在全体 Promise 都为 fulfilled 的情况下,新的实例才会变成 fulfilled 状态。;如果参数中 Promise 有一个失败(rejected),此实例回调失败(rejecte),失败原因的是第一个失败 Promise 的结果。

举个例子:

Promise.all([
  new Promise(resolve => {
    setTimeout(resolve, 1000, "p1");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 2000, "p2");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 3000, "p3");
  })
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error);
  });
// [p1,p2,p3]

Promise.all([
  new Promise(resolve => {
    setTimeout(resolve, 1000, "p1");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 2000, "p2");
  }),
  Promise.reject("p3 error")
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error);
  });
// p3 error
登入後複製

获取 cnode 社区的 精华贴的前十条内容

fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10")
  .then(res => res.json())
  .then(res => {
    const fetchList = res.data.map(item => {
      return fetch(`https://cnodejs.org/api/v1/topic/${item.id}`)
        .then(res => res.json())
        .then(res => res.data);
    });
    Promise.all(fetchList).then(list => {
      console.log(list);
    });
  });
登入後複製

Promise.race() 竞态执行

Promise.race 也会将多个 Promise 实例封装成一个新的Promise实例,只不过新的 Promise 的状态取决于最先改变状态的 Promise 实例的状态。

在前端最典型的一个用法是为 fetch api 模拟请求超时。

Promise.race([
  fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10").then(res =>
    res.json()
  ),
  new Promise((resolve, reject) => {
    setTimeout(reject, 1, "error");
  })
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error); // 进入这里
  });
登入後複製

上述例子中只要请求 未在 1 毫秒内结束就会进入 .catch() 方法中,虽然不能将请求取消,但是超时模拟却成功了

Promise.resolve(value) && Promise.reject(reason)

这两个方法都能用来创建并返回一个新的 Promise , 区别是 Promise.resolve(value) 携带进新的 Promise 状态是 fulfilled。而 Promise.reject(reason) 带来的 rejected

有的时候可以用来简化一些创建 Promise 的操作如:

const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time));
// 这里创建一个 睡眠,并且打印的链
Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => sleep(1000))
  .then(() => {
    console.log(2);
  })
  .then(() => sleep(2000))
  .then(() => {
    console.log(3);
  });
登入後複製

有时也用来 手动改变 Promise 链中的返回状态 ,当然这样实际上和 直接返回一个值,或者是 使用 throw Error 来构造一个错误,并无区别。到底要怎么用 就看个人喜好了

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("resolve"); // 1.
  }, 1000);
})
  .then(result => {
    return Promise.reject("reject1"); // 2.
  })
  .then(
    result => {
      return Promise.resolve(result + "2");
    },
    err => {
      return Promise.resolve(err); // 3.
    }
  )
  .then(res => {
    console.log(res); // 4.
  })
  .catch(err => {
    console.log(err + "err");
  });
// reject1
登入後複製

几个例子

下面来看几个例子:

关于执行顺序,具体可搜索,js 循环

new Promise((resolve, reject) => {
  console.log("step 1");
  resolve();
  console.log("step 2");
}).then(() => {
  console.log("step 3");
});
console.log("step 4");

// step 1, step 2, step 4 , step 3
登入後複製

在使用 Promise 构造函数构造 一个 Promise 时,回调函数中的内容就会立即执行,而 Promise.then 中的函数是异步执行的。

关于状态不可变更

let start;
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    start = Date.now();
    console.log("once");
    resolve("success");
  }, 1000);
});
p.then(res => {
  console.log(res, Date.now() - start);
});
p.then(res => {
  console.log(res, Date.now() - start);
});
p.then(res => {
  console.log(res, Date.now() - start);
});
登入後複製

Promise 构造函数只执行一次,内部状态一旦改变,有了一个值,后续不论调用多少次then()都只拿到那么一个结果。

关于好像状态可以变更

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

const p2 = p1.then((resolve, reject) => {
  throw new Error("error");
});

console.log("p1", p1);
console.log("p2", p2);

setTimeout(() => {
  console.log("p1", p1);
  console.log("p2", p2);
}, 2000);
登入後複製

观察这一次的打印
第一次打印出两个 Promise 的时候都是 pending ,因为 p2 是基于 p1 的结果,p1 正在 pending ,立即打印出的时候肯定是 pending ;第二次打印的时候,因为 p1 的状态为 resolved ,p2 为 rejected ,这个并不是已经为 fulfilled 状态改变为 rejected ,而是 p2 是一个新的 Promise 实例,then() 返回新的 Promise 实例。

关于透传

Promise.resolve(11)
  .then(1)
  .then(2)
  .then(3)
  .then(res => {
    console.info("res", res);
  });
//   11
登入後複製

给 then 方法传递了一个非函数的值,等同于 then(null),会导致穿透的效果,就是直接过掉了这个 then() ,直到符合规范的 then() 为止。

Promise 的串行调用

使用 Array.reduce 方法串行执行 Promise

const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time));
[1000, 2000, 3000, 4000].reduce((Promise, item, index) => {
  return Promise.then(res => {
    console.log(index + 1);
    return sleep(item);
  });
}, Promise.resolve());
// 在分别的等待时间后输出 1,2,3,4
登入後複製

这篇文章到这里就基本上结束了,相信 如果能理解上面的内容,并且在实际项目中使用的话。应该会让工作更高效吧,对于新的异步使用应该也会更加的得心应手。Promise 的使用相对简单,可能后续再出一篇如何实现一个 Promise 吧。

以上是Promise是什麼? Promise的介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板